code you can branch on.
Authentication
Every/v1 endpoint takes a Goro API key as a Bearer token. Keys start with
goro_live_ and are created in the dashboard at
app.usegoro.ai.
GET /v1/whoami is the cheapest way to check that a key works. It reads
nothing beyond the key row and costs nothing.
A missing, malformed, unknown or revoked key answers 401 with the code
unauthorized. Unknown and revoked keys answer identically, so a probe cannot
learn whether a key exists.
The MCP server at
https://mcp.usegoro.ai/mcp authenticates with OAuth
access tokens instead, not with goro_live_ keys. See the
MCP quickstart.Requests
POST bodies must be a JSON object. A body that is not valid JSON, or is an
array or a scalar, answers 400 validation_error before anything else runs.
Every response carries an x-request-id header. Send your own and Goro echoes
it back; omit it and Goro mints one. Quote it in support email.
Money
Every amount appears twice: as an integer in micro-USD, and as a human string._usd strings use the fewest decimals that represent the
amount exactly, so you will see $4.00, $0.0030 and $0.000001.
Do arithmetic on the _micro integers. Show the _usd strings to people.
Run results are held only until you collect them
Goro records what a run cost, how many rows it produced and when it ran, and keeps that permanently. The rows themselves are held so a slow run stays collectable, then deleted shortly after your first successful fetch and unconditionally 24 hours after the run finished. That shapes how you get results back:- A synchronous
POST /v1/runreturns the rows inline in its200body. This is the normal path and the one to build on. - An asynchronous run returns
202, and theGET /v1/runs/{id}poll that finds the run finished returns the rows in that response. GET /v1/runs/{id}works whether the run is still finishing or already settled, because a finished run keeps its rows until you take them.- Once you have collected them, or once the run is a day old, they are gone.
Goro falls back to the provider’s own copy at that point, which is best
effort:
output: nullon an old run is a normal outcome, not an error.
Media results expire too
Some tools answer with a link instead of data, and the same clock applies. In one case it applies to the file as well.- Voice calls return a signed URL to audio that Goro hosts. It stops working 24 hours after the run, and the audio itself is deleted on that same clock. There is no way to renew the link.
- Image calls return URLs hosted by kie.ai, kept on kie.ai’s own schedule.
Pagination
GET /v1/runs and GET /v1/wallet/activities are cursor-paginated, newest
first.
Responses carry
items and next_cursor. A next_cursor of null means
that was the last page.
400 validation_error with the message
invalid cursor.
Errors
Every error body is{"code": "...", "message": "..."} plus any extra fields
listed below. The code is stable and the thing to branch on. The message
is written for a human and can change.
required_micro and available_micro on a 402 are present when Goro knows
them, which is not on every path, so treat both as optional.
429 is currently raised by the OAuth endpoints, which throttle per IP. The
/v1 endpoints do not enforce a request rate limit today. Handle the status
anyway: the shape above is what you will get when they do.Validation errors
400 validation_error carries an errors array, one entry per problem, so an
agent can correct every field in one pass instead of one round trip per
mistake.
Tool inputs are validated as JSON Schema draft 7, with no type coercion. The
string
"10" is not the integer 10, and sending it fails rather than being
quietly fixed. Schema defaults are filled in for you before the run is quoted.
What an error costs
Nothing, on every path above. A run that fails validation never reaches the provider and never places a hold. A run that places a hold and then fails, times out or is stopped has the entire hold released and settles at a final cost of zero.Start with discover
Find the tool that answers your question, with its price.