Usero Journal
Feedback API: Collect Feedback From Any Platform With One HTTP POST
A feedback API is an HTTP endpoint you POST to in order to record user feedback, instead of relying on a JavaScript widget. It is what you reach for when the feedback comes from somewhere a browser widget cannot run: a native iOS or Android app, a Python backend, a CLI, or a desktop app.
Most feedback tools ship a JS snippet, drop it in your HTML, done. That snippet is useless the moment your product is not a web page. There is no DOM in a Swift view, no browser in a backend job, no widget mount point in a terminal. If you want to capture feedback from those surfaces, you need a plain HTTP endpoint you can call from any language. This guide covers what a good feedback API looks like, with copy-pasteable examples in curl, Swift, Python, and JavaScript.
For context: I am Will, I build Usero. The endpoint below is the real one Usero exposes, and it is the same endpoint our widget uses under the hood, so nothing here is a special degraded mode.
Why a JavaScript Widget Is Not Enough
A widget is the right answer for a web app. It mounts in the page, captures the URL and a screenshot, and your users never leave. The problem is everything that is not a web page in a browser.
- Native apps. An iOS app in Swift or an Android app in Kotlin has no DOM. You cannot embed a web widget in a native view without an ugly webview hack, and even then it will not feel native. A direct HTTP call from your settings screen is the clean path.
- Server-side and backend jobs. A failed import, a flaky third-party call, a batch job that produced a weird result, these are worth capturing as feedback, and there is no user sitting in a browser to click anything. Your code posts the report itself.
- CLIs and desktop apps. A command-line tool can prompt for a quick rating after a run and POST it. An Electron or native desktop app can do the same from its own UI. No web page involved.
In every one of these cases the missing piece is the same: a plain HTTP endpoint that takes a JSON body. Once you have that, the surface the feedback came from stops mattering.
What a Good Feedback API Looks Like
Not all collection endpoints are equal. Here is what makes one pleasant to use from any platform, and what Usero’s endpoint does.
- One POST, JSON body. No SDK to install, no client library to keep up to date. If your language can make an HTTPS request, it can send feedback.
- CORS wide open. The endpoint returns
Access-Control-Allow-Origin: *, so a browser fetch, a mobile request, and a server call all behave the same. - Client id, not a secret key. The
clientIdin the body identifies your project. It is safe to ship inside a mobile binary, so you skip the API-key rotation dance. - Rating and/or comment. Send a numeric rating, a free-text comment, or both. The only hard rule is that at least one of them is present.
- Attach context with metadata. An arbitrary JSON
metadataobject (10KB max serialized) carries app version, OS, build number, or feature flags, so every report arrives with what you need to reproduce it.
A Minimal Request
The smallest valid call is a clientId and one of rating or comment. The rating scale is 1 to 4: 1 Terrible, 2 Bad, 3 Good, 4 Amazing.
curl -X POST https://usero.io/api/feedback \
-H "Content-Type: application/json" \
-d '{"clientId": "YOUR_CLIENT_ID", "rating": 3}'A successful response looks like this:
{ "success": true, "feedbackId": "abc123" }A Full Request With Context
In practice you want more than a bare rating. Add the comment, the user’s email so you can reply, the page or screen they were on, and a metadata object with the version and platform details that make a bug reproducible.
curl -X POST https://usero.io/api/feedback \
-H "Content-Type: application/json" \
-d '{
"clientId": "YOUR_CLIENT_ID",
"rating": 2,
"comment": "Export to CSV times out on large projects",
"userEmail": "jamie@example.com",
"pageUrl": "https://yourapp.com/projects/42/export",
"pageTitle": "Export project",
"metadata": {
"appVersion": "2.4.1",
"os": "macOS 15.2",
"build": 1842
}
}'The optional environment field is worth a note: omit it unless you separate environments like production and staging. Feedback without it lands in your default inbox, which is what you want most of the time.
The Same Call From Swift, Python, and JS
Because it is one POST with a JSON body, the call translates directly into any language. Here is the in-app feedback request from a native iOS app in Swift, where metadata pulls the build and OS version straight off the device.
var request = URLRequest(url: URL(string: "https://usero.io/api/feedback")!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONSerialization.data(withJSONObject: [
"clientId": "YOUR_CLIENT_ID",
"rating": 2,
"comment": "Crash on opening the export sheet",
"metadata": [
"appVersion": Bundle.main.infoDictionary?["CFBundleShortVersionString"] ?? "unknown",
"os": ProcessInfo.processInfo.operatingSystemVersionString,
],
])
let (data, _) = try await URLSession.shared.data(for: request)Server-side from Python, for a backend job posting feedback about itself:
import requests
requests.post(
"https://usero.io/api/feedback",
json={
"clientId": "YOUR_CLIENT_ID",
"comment": "Nightly sync skipped 12 records with no error",
"metadata": {"job": "nightly-sync", "build": 1842},
},
)And from JavaScript, where you can pull the page details off the browser:
await fetch('https://usero.io/api/feedback', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
clientId: 'YOUR_CLIENT_ID',
rating: 4,
comment: 'Love the new dashboard',
userEmail: 'jamie@example.com',
pageUrl: window.location.href,
pageTitle: document.title,
metadata: { appVersion: '2.4.1', os: navigator.platform },
}),
})Handling Errors
Three error responses are worth knowing about so your client code can react instead of swallowing failures silently.
- 400, invalid data. Validation failed. The body carries an
issuesobject naming each field that failed. The usual culprit is sending neither a rating nor a non-empty comment, since one of them is required. - 400, metadata too large. Your serialized
metadataexceeds 10KB. Trim it down, the metadata is for version and platform context, not for dumping full logs. - 403, domain not allowed. Your client has a domain allowlist and this request’s origin is not on it. Add the origin in Settings, or clear the allowlist if you do not need it. New clients have no allowlist, so you will not hit this until you add one.
A 500 means something broke on our side and is safe to retry. For everything else, read the error string in the JSON body, it tells you what to fix.
Where API Feedback Goes Next
A POST endpoint is table stakes. The part that matters is what happens after the feedback lands. In Usero, feedback that arrives via the API is treated like feedback from anywhere else. It clusters with your widget feedback, your Slack feedback, and your GitHub feedback, so ten reports of the same bug from three different surfaces become one item with clear demand behind it.
The endpoint is the boring part. The point is that a backend job and a real user can flag the same problem, and you see it as one thing instead of two.
From there a clustered item can become a GitHub pull request, a first pass at the fix, opened against your repo for you to review and merge. You read the diff, you decide. Nothing ships on its own, and nothing merges automatically. The API is where the signal comes in; the PR is where it turns into a change you can actually evaluate.
How To Start
- Find your clientId. Create a client in Usero and grab the id that starts with
client_. That is the only credential you need. - POST a test request. Run the minimal curl above with your real clientId. A 200 with
success: truemeans you are wired up. - Check the inbox. Open your Usero inbox and the test feedback is there, alongside whatever the widget and your other sources have collected.
Related Reading
- How To Collect User Feedback In-AppRead
- Collecting User Feedback in a Next.js AppRead
- How To Embed a Feedback Widget in ReactRead
- The Feedback Tool That Opens a GitHub PR for YouRead
If You Want To Try Usero
Usero gives you one endpoint to collect feedback from any surface, a widget for the web, and an HTTP POST for everything the widget cannot reach. It all lands in one inbox, clusters automatically, and a clustered request can open a GitHub pull request you review and merge. Try it free and send your first API feedback in a few minutes.
Frequently Asked Questions
What is a feedback API?
A feedback API is an HTTP endpoint you POST to in order to record a piece of user feedback, instead of relying on a JavaScript widget embedded in a web page. You send a JSON body with a rating or a comment (or both) and the feedback shows up in your inbox. Because it is a plain HTTP call, it works from a native iOS or Android app, a Python backend, a CLI, a desktop app, or a cron job, anywhere a browser widget cannot run.
How do I collect feedback via a REST API?
With Usero you POST to https://usero.io/api/feedback with Content-Type application/json. The body must include your clientId (it starts with client_) and at least one of rating (an integer 1 to 4) or a non-empty comment. There is no API key and no auth header to manage; the clientId in the body identifies your project. A successful call returns {"success": true, "feedbackId": "..."}.
Do I need an API key to send feedback?
No. The clientId in the request body is what identifies your project, and it is not a secret in the way an API key is, so it is safe to ship inside a mobile binary or client-side code. If you want to lock down who can submit, add a domain allowlist in Settings; requests from origins that are not on the list get a 403. New clients have no allowlist, so every request is accepted until you add one.
Can I send in-app feedback from a native mobile app?
Yes. The endpoint sets Access-Control-Allow-Origin: * and accepts a plain JSON POST, so a Swift URLSession call or a Kotlin OkHttp call works the same as a browser fetch. Put the app version, OS version, and build number in the metadata object (10KB max serialized) so every report from your iOS or Android users arrives with the context you need to reproduce it.
What happens to feedback I send through the API?
It lands in the same inbox as feedback from the widget, Slack, GitHub, or a CSV import, and it clusters together with all of it. Ten reports of the same bug from your API, your widget, and Slack group into one item, so you see real demand instead of a pile of near-duplicates. A clustered item can then become a GitHub pull request, a first pass at the fix that you review and merge. Nothing merges on its own.
Why am I getting a 400 error when I POST feedback?
The most common cause is sending neither a rating nor a non-empty comment; one of them is required. The 400 response includes an issues object that names each failing field, so check it to see exactly what was rejected. The other 400 you can hit is "Metadata too large (max 10KB)", telling you the serialized metadata object is over the limit and needs trimming.
Continue reading
User Testing Tool: How to Pick One (and When You Do Not Need One)
A user testing tool records a real person using your product. Two questions pick the right one: do you need a moderator on the call, and do you need a recruited panel? Compare UserTesting, Maze, Lookback, Useberry, and Usero, with honest pricing and a when-to-skip-it.
8 min read
How to Import GitHub Issues Into a Feedback Tool (and Why)
GitHub issues pile up and never dedupe. Import them into a feedback tool that clusters by meaning across every source, counts what recurs, and can open a GitHub PR you review and merge. A practical guide.
7 min read
Session Replay Tool: How to Pick the Right One (2026)
Picking a session replay tool comes down to one question: what do you actually need to watch? Compare FullStory, LogRocket, PostHog, Hotjar, Clarity, and Usero by the job they fit, with real pricing and an honest when-to-skip-it.
9 min read
Build a feedback loop your team actually uses
Usero collects, clusters, and turns user feedback into shipped fixes.
Get started free