Skip to main content
The Goro HTTP API is ten endpoints over one base URL. Everything is JSON in and JSON out, and every response is either the documented success body or an error body with a 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.
One US dollar is 1,000,000 micro. Amounts are never floats, on the wire or in the database. The _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/run returns the rows inline in its 200 body. This is the normal path and the one to build on.
  • An asynchronous run returns 202, and the GET /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: null on an old run is a normal outcome, not an error.
Keep the rows from the response that hands them to you.

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.
One rule covers both, and it is the one to build on: collect your result within 24 hours, and download the file rather than storing the link. A URL you saved is not a copy of the thing it points at.

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.
Cursors are opaque base64url strings. Do not construct or parse them. A cursor Goro did not issue answers 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.