# Discover Source: https://docs.usegoro.ai/api/discover POST /v1/discover. Rank the catalog against a plain-English query and get each match with its price and a fillable sample input. ``` POST https://api.usegoro.ai/v1/discover ``` Describe the job in plain language. Goro ranks the catalog against it and returns the best matches, each with its price and a sample input you can fill in, so you can often go straight to [`run`](/api/run) without inspecting first. ## Request | Field | Type | Required | Notes | | ------- | ------- | -------- | ------------------------------------------------------------------------------- | | `query` | string | yes | What you are trying to accomplish. Must be non-empty | | `limit` | integer | | How many matches to return. Default 5, capped at 20. Must be a positive integer | ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/discover \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "tweets about ai agents", "limit": 3}' ``` ## Response ```json theme={null} { "items": [ { "slug": "twitter.search", "name": "Twitter search", "category": "social", "when_to_use": "Use when you need tweets about a topic, sentiment or chatter on X, mentions of a brand or person, or what people are saying right now.", "price": { "type": "usage_based", "min_micro": 2000, "min_usd": "$0.0020", "max_micro": 3000000, "max_usd": "$3.00", "billed_on_actual_usage": true, "per_unit_micro": 1200, "per_unit_usd": "$0.0012", "unit_label": "result", "summary": "$0.0012 per result, billed on actual usage (minimum $0.0020 per call), up to $3.00 per call" }, "sample_input": { "sort": "Latest", "maxItems": 20, "searchTerms": "" } } ] } ``` | Field | Type | Notes | | ---------------------- | ------ | -------------------------------------------------------- | | `items` | array | Matches, best first. Can be empty | | `items[].slug` | string | The identifier to pass to `inspect` and `run` | | `items[].name` | string | Human name | | `items[].category` | string | Catalog category, for example `social`, `search`, `maps` | | `items[].when_to_use` | string | Guidance on when this tool is the right one | | `items[].price` | object | See below | | `items[].sample_input` | object | A ready-to-fill input object | Ranking is lexical and deterministic: the same query returns the same order every time. Matches that score zero are dropped, so a query nothing answers returns `{"items": []}` rather than a list of bad guesses. ### sample\_input `sample_input` is built from the tool's own input schema. It contains every required field, plus every optional field that declares a default. Values come from the schema's examples, then its defaults, then its enums. A required field with none of those appears as a placeholder like `""`, which you are expected to replace. It is a starting point, not a validated input. `run` validates what you actually send. ### price `price` is the same object [`inspect`](/api/inspect) and [`run`](/api/run) return. Its fields depend on `type`. | Field | Type | Present on | Notes | | ------------------------------------ | --------------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | | `type` | string | all | `usage_based`, `metered`, `per_call`, `per_call_variant` or `per_result` | | `summary` | string | all | One-line human description | | `per_unit_micro`, `per_unit_usd` | integer, string | `usage_based`, `metered` | The rate you pay per unit. Absent on a `usage_based` tool with no published rate | | `unit_label` | string | `usage_based`, `metered` | What one unit is: `profile`, `page`, `second of audio` | | `min_micro`, `min_usd` | integer, string | `usage_based`, `metered` | Floor for a call that delivered something | | `max_micro`, `max_usd` | integer, string | `usage_based`, `metered` | Ceiling. You are never charged above it | | `billed_on_actual_usage` | boolean | `usage_based`, `metered` | Always `true`. The total depends on the call, so `max_usd` is a maximum and not a price | | `base_micro`, `base_usd` | integer, string | `per_call`, `per_call_variant`, `per_result` | Flat component. On `per_call` it is the entire price; on `per_call_variant` it is `default_variant`'s price | | `selector` | string | `per_call_variant` | The input field whose value picks the price, for example `resolution` | | `variants` | array | `per_call_variant` | Every priced option, cheapest first: `{value, price_micro, price_usd}`. Each is an exact price, not a starting point | | `default_variant` | string | `per_call_variant` | The option billed when you omit `selector` from the input | | `per_result_micro`, `per_result_usd` | integer, string | `per_result` | Charged per row | | `max_results_cap` | integer | `per_result` | Rows past this are neither delivered nor billed | Most of the catalog is `usage_based`: the total follows what the call actually did, floored at `min_micro` and capped at `max_micro`. The image tools are `per_call`, one flat price with no ceiling to read. The voice tools are `metered`, an exact rate against the size of your request, with a ceiling on top. The nano banana image tools are `per_call_variant`: flat and exact like `per_call`, but the price depends on which `resolution` you ask for, and every option's price is listed in `variants`. `per_result` is part of the contract but nothing in the catalog uses it today, so handle it if you parse `price` generically. A settled run that cost nothing is charged nothing: `cost_micro` comes back `0` and `free` comes back `true`. See [run](/api/run). On a `usage_based` or `metered` tool, `max_usd` is a ceiling and the hold Goro places is sized from what you request, so it usually sits well below that number. On a `per_call` tool there is no ceiling: `base_usd` is what the call costs. Every tool's page in the [catalog](/tools/overview) says which it is. ## Errors | Status | `code` | When | | ------ | ------------------ | -------------------------------------------------------------- | | 400 | `validation_error` | `query` missing or empty, or `limit` is not a positive integer | | 401 | `unauthorized` | Missing or invalid API key | Discovery is free. It places no hold and charges nothing. Read the full input schema before you run it. # Downloads Source: https://docs.usegoro.ai/api/downloads Every generated file gets a Goro link that saves it. One address, one expiry, and nothing re-hosted. ``` GET https://files.usegoro.ai/ ``` Runs that produce files — video, images, generated speech — come back with an `output_downloads` array alongside `output`. Each entry is a link on Goro's own domain that downloads the file when opened. No API key. The token is the authorization, which is what makes these links something you can hand to a person, put in an email, or open on a phone that has never signed in to Goro. ## What a run returns ```json theme={null} { "run_id": "3f2a...", "endpoint": "video.grok", "status": "COMPLETED", "output": ["https:///tasks/9f2/clip.mp4"], "output_count": 1, "output_downloads": [ { "index": 0, "filename": "clip.mp4", "url": "https://files.usegoro.ai/eyJlIjoxNzg1...Kd2Qh8Lm" } ], "cost_usd": "$0.40" } ``` | Field | Type | Notes | | ---------- | ------- | ------------------------------------------------------------------------------------------------------------------------ | | `index` | integer | Which item of `output` this is. Stays the index of the original array, so it lines up even when some items are not files | | `filename` | string | What the browser will save it as | | `url` | string | The download link. Opening it saves the file | `output_downloads` is absent entirely when a run produced no files, which is most of them. An endpoint that returns scraped rows has exactly the result shape it always had. The provider's own URL stays in `output`, untouched. Use it when you want to stream the file into a video player or fetch it yourself; use the download link when a person is going to click it. ## Why not just use the provider's URL Three things the raw link cannot do. **It is not our address.** It is a hostname nobody recognises, on a CDN we do not control. If Goro changes providers, every link already handed out is a dead end. A `files.usegoro.ai` link keeps working across that. **It does not reliably download.** Whether clicking a URL saves a file or opens a player is decided by the `Content-Disposition` header, which is set by the server holding the file. On a link this route serves, that header is ours. **It expires on somebody else's schedule and says nothing when it does.** A Goro link expires on our clock and, when the file really is gone, answers with a sentence explaining which retention ran out. ## Nothing is re-hosted The file stays exactly where the provider put it. Goro does not copy generated media into its own storage, and this route did not change that: it resolves the token to a run you own, reads the URL that run already recorded, and either redirects you to it or streams it through with a download header attached. That is a deliberate limit rather than an optimisation. Re-hosting output would mean a new bucket, a new retention clock and a standing store of customer files that the [Privacy Policy](https://usegoro.ai/legal/privacy) would have to start describing. It does not exist, so there is nothing to describe. ## Playing instead of saving Add `?inline=1` to get a redirect to the file where it actually lives, with no download header and Goro out of the path: ``` https://files.usegoro.ai/?inline=1 ``` Use it to embed a video, to stream rather than save, or for a very large file on a slow connection where a proxied download might not finish. ## How long a link lasts Seven days from when the run returned it, and shorter in practice, because the file itself has to still exist: | What | How long | | ------------------------------------------- | ---------------------------------------------------- | | The download link | 7 days | | Generated video and images, at the provider | 14 days | | Generated speech, in Goro's storage | 24 hours | | A run's cached output, in Goro's database | 24 hours, after which the provider's copy is re-read | The practical rule: collect what you need soon after the run. A link that outlives its file answers `410` with an explanation rather than a broken download. ## Errors These are HTML pages, not JSON envelopes, because a person clicked the link. | Status | When | | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 404 | The token is expired, malformed or was tampered with, or the run no longer exists. The cases answer identically so the error cannot be used to learn which links are real | | 410 | The file has aged out at the provider, or that output item is not a file | | 502 | The provider holding the file did not answer. Retrying usually works | ## Security A download link is a capability: whoever holds it can fetch that one file until it expires. Treat it the way you would treat any signed URL. * The token names a run id and an index, never a URL, so it cannot be edited to point Goro at an address it did not produce. * It is signed, so a token built by hand does not verify. * It resolves only inside the workspace that owns the run. * It is scoped to a single output item, not to the run. * Responses are sent `private, no-store`, so nothing in between keeps a copy. Uploads, handles, and the page a user opens to hand your agent a file. # Inspect Source: https://docs.usegoro.ai/api/inspect POST /v1/inspect. Read one tool's real input schema, a fillable sample input, an output sample and its price. ``` POST https://api.usegoro.ai/v1/inspect ``` Everything you need to call a tool correctly on the first try. Call this when you know the slug but not the exact input shape. ## Request | Field | Type | Required | Notes | | ------ | ------ | -------- | ---------------------------------------------- | | `slug` | string | yes | The tool identifier, for example `maps.places` | ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/inspect \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug": "maps.places"}' ``` ## Response ```json theme={null} { "slug": "maps.places", "name": "Google Maps places", "category": "maps", "description": "Search Google Maps for places matching a query and get back name, address, rating, review count, phone, website, and coordinates.", "when_to_use": "Use when you need local businesses or points of interest, lead lists for a city or area, or place details like ratings and contact info.", "input_schema": { "type": "object", "required": ["searchStringsArray"], "properties": { "language": { "type": "string", "default": "en" }, "locationQuery": { "type": "string" }, "searchStringsArray": { "type": "array", "items": { "type": "string" } }, "maxCrawledPlacesPerSearch": { "type": "integer", "default": 20 } } }, "sample_input": { "language": "en", "searchStringsArray": "", "maxCrawledPlacesPerSearch": 20 }, "output_sample": { "name": "Origo Coffee", "phone": "+40 21 000 0000", "rating": 4.6, "address": "Strada Lipscani 9, Bucharest", "website": "https://origocoffee.ro" }, "price": { "type": "usage_based", "min_micro": 2000, "min_usd": "$0.0020", "max_micro": 6000000, "max_usd": "$6.00", "billed_on_actual_usage": true, "per_unit_micro": 9000, "per_unit_usd": "$0.0090", "unit_label": "place", "summary": "$0.0090 per place, billed on actual usage (minimum $0.0020 per call), up to $6.00 per call" } } ``` | Field | Type | Notes | | --------------- | ------ | ------------------------------------------------------------ | | `slug` | string | The identifier you passed | | `name` | string | Human name | | `category` | string | Catalog category | | `description` | string | What the tool does | | `when_to_use` | string | When this tool is the right choice | | `input_schema` | object | JSON Schema draft 7. The real schema, validated on every run | | `sample_input` | object | Required fields plus optional fields that have defaults | | `output_sample` | object | One illustrative row of what the tool returns | | `price` | object | Same shape as in [`discover`](/api/discover#price) | On the passthrough tools, which is most of the catalog, Goro hands your input to the underlying tool unchanged and hands back its rows untouched. `input_schema` is that tool's own schema, not a translation, so any field the tool accepts works even when the schema does not name it: undeclared fields pass through rather than being rejected. The image and voice tools are wrapped rather than passed through. Their schemas set `additionalProperties: false`, and a field the schema does not name comes back as a `validation_error`. Read the flag rather than assuming either behaviour. `inspect` deliberately does not expose provider internals. There is no field naming which upstream service or actor runs the job. ## Errors | Status | `code` | When | | ------ | ------------------ | ------------------------------------------------------------------------------------ | | 400 | `validation_error` | `slug` missing or empty | | 401 | `unauthorized` | Missing or invalid API key | | 404 | `not_found` | Unknown slug, or a tool that is not active. Message reads `unknown endpoint: {slug}` | Inspection is free. It places no hold and charges nothing. Send the input, get the rows. # API overview Source: https://docs.usegoro.ai/api/overview Base URL, authentication, money format, pagination and the full error contract. 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. ``` https://api.usegoro.ai ``` | Endpoint | Method | What it does | | -------------------------------------- | ------ | ------------------------------------------------------------------ | | [`/v1/discover`](/api/discover) | POST | Rank the catalog against a plain-English query | | [`/v1/inspect`](/api/inspect) | POST | Read one tool's input schema, output sample and price | | [`/v1/run`](/api/run) | POST | Run a tool | | [`/v1/uploads`](/api/uploads) | POST | Get a handle and a URL to PUT a file to, for a tool that takes one | | [`/v1/runs`](/api/runs) | GET | List your runs, newest first | | [`/v1/runs/{id}`](/api/runs) | GET | Fetch or advance one run | | [`/v1/runs/{id}/stop`](/api/runs) | POST | Stop a run that is still going | | [`/v1/wallet/balance`](/api/wallet) | GET | Current balance | | [`/v1/wallet/activities`](/api/wallet) | GET | The wallet ledger | | `/v1/whoami` | GET | Which workspace this key belongs to | Signing up needs no plan, and neither does discovery: `discover`, `inspect`, listing and fetching runs, and reading the wallet all work on a workspace with no plan. `POST /v1/run` and `POST /v1/uploads` need an active plan, Build or Scale; without one they answer `402 subscription_required`. See [Errors](#errors) below and [Pricing](/pricing#plan-rules). ## 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](https://app.usegoro.ai). ```bash theme={null} curl https://api.usegoro.ai/v1/whoami \ -H "Authorization: Bearer $GORO_API_KEY" ``` ```json theme={null} { "workspace_id": "0f1e2d3c-4b5a-6978-8796-a5b4c3d2e1f0", "key_label": "production agent", "created_at": "2026-07-14T09:12:44.183Z" } ``` `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](/guide/quickstart-mcp). ## 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. ```json theme={null} { "available_micro": 1250000, "available_usd": "$1.25" } ``` 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. | Parameter | Type | Notes | | --------- | ------- | --------------------------------------------------- | | `limit` | integer | Default 20, maximum 100. Must be a positive integer | | `cursor` | string | The `next_cursor` from the previous page | Responses carry `items` and `next_cursor`. A `next_cursor` of `null` means that was the last page. ```bash theme={null} curl "https://api.usegoro.ai/v1/runs?limit=50" \ -H "Authorization: Bearer $GORO_API_KEY" ``` 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. ```json theme={null} { "code": "insufficient_funds", "message": "Insufficient funds: this run requires a hold of $0.1200. Top up to continue.", "top_up_url": "https://app.usegoro.ai/billing", "required_micro": 120000, "plan": "free", "renews_at": null } ``` | Status | `code` | Extra fields | What happened | | ------ | ----------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | 400 | `validation_error` | `errors` | The request body or the tool input failed validation | | 401 | `unauthorized` | | Missing, malformed, unknown or revoked API key | | 402 | `subscription_required` | `billing_url`, `top_up_url` | The workspace has no active plan. `POST /v1/run` and `POST /v1/uploads` need one; nothing is held or charged | | 402 | `insufficient_funds` | `top_up_url`, `required_micro`, `available_micro`, `plan`, `renews_at` | The wallet cannot cover the hold. On a plan, `renews_at` is when included credit returns and the message names that date | | 402 | `budget_exceeded` | `period`, `cap_micro`, `spent_micro` | A workspace budget cap would be breached | | 403 | `forbidden` | | The credential is not allowed to do this. On the MCP server, a token missing the tool's scope | | 404 | `not_found` | | Unknown tool slug, or a run that is not yours | | 409 | `conflict` | | The run is already finished, so it cannot be stopped | | 429 | `rate_limited` | `retry_after_seconds` | Too many requests. A `Retry-After` header comes with it | | 500 | `internal_error` | | Something broke on our side. Nothing internal is disclosed | | 502 | `provider_error` | | The underlying tool failed. The hold is released, you are not charged | | 503 | `service_unavailable` | | Temporary. Retry shortly | `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. ```json theme={null} { "code": "validation_error", "message": "Input for maps.places failed validation (2 errors)", "errors": [ { "path": "/searchStringsArray", "message": "must have required property 'searchStringsArray'", "keyword": "required", "params": { "missingProperty": "searchStringsArray" } }, { "path": "/maxCrawledPlacesPerSearch", "message": "must be integer", "keyword": "type", "params": { "type": "integer" } } ] } ``` | Field | Type | Notes | | --------- | ------ | ------------------------------------------------------------------------------- | | `path` | string | JSON pointer into your input. Empty string for problems with the request itself | | `message` | string | What is wrong with that field | | `keyword` | string | The JSON Schema keyword that failed: `required`, `type`, `maximum`, and so on | | `params` | object | The keyword's own details, verbatim | 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. Find the tool that answers your question, with its price. # Run Source: https://docs.usegoro.ai/api/run POST /v1/run. Execute a tool, get the rows inline when it is fast, or a run id to poll when it is not. ``` POST https://api.usegoro.ai/v1/run ``` The only endpoint that spends money. Goro validates your input, holds an amount sized from your request, runs the tool, then charges what it actually cost and releases the rest. Running a tool needs an active plan, Build or Scale. Without one this answers `402 subscription_required` before anything is held, and nothing is charged. See [Pricing](/pricing#plan-rules). ## Request | Field | Type | Required | Notes | | ------- | ------ | -------- | --------------------------------------------------------------- | | `slug` | string | yes | The tool identifier | | `input` | object | | The tool's input, matching its `input_schema`. Defaults to `{}` | ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "slug": "web.search", "input": { "queries": "best espresso machines", "maxPagesPerQuery": 1 } }' ``` `input` must be a JSON object. An array, a string or a number answers `400` with the path `/input`. Your input is validated against the tool's schema before anything is held or started, and schema defaults are filled in for you. ## Responses There are three of them, and which one you get depends on how fast the tool finished. ### 200: finished The tool completed inside the synchronous window, which is 25 seconds by default. The rows are in `output`, abridged to one row here. ```json theme={null} { "run_id": "8c3a1f42-2d7b-4a15-9f0e-6b2c8d5e7a91", "endpoint": "web.search", "status": "COMPLETED", "output": [ { "url": "https://example.com/espresso", "title": "The best espresso machines of 2026", "snippet": "We tested eleven machines over six weeks...", "position": 1 } ], "output_count": 10, "quoted_cost_micro": 45000, "quoted_cost_usd": "$0.0450", "cost_micro": 10500, "cost_usd": "$0.0105", "error": null, "created_at": "2026-07-30T11:04:21.552Z", "started_at": "2026-07-30T11:04:21.998Z", "finished_at": "2026-07-30T11:04:29.140Z", "price": { "type": "usage_based", "min_micro": 2000, "min_usd": "$0.0020", "max_micro": 1000000, "max_usd": "$1.00", "billed_on_actual_usage": true, "per_unit_micro": 7500, "per_unit_usd": "$0.0075", "unit_label": "page", "summary": "$0.0075 per page, billed on actual usage (minimum $0.0020 per call), up to $1.00 per call" } } ``` | Field | Type | Notes | | ----------------------------------------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `run_id` | string | UUID for this run | | `endpoint` | string | The slug that ran | | `status` | string | One of `QUEUED`, `RUNNING`, `COMPLETED`, `FAILED`, `STOPPED`, `TIMED_OUT` | | `output` | array or null | The tool's rows, untouched | | `output_count` | integer or null | How many rows the run produced | | `output_downloads` | array | Present only when the run produced FILES. One `{index, filename, url}` per file, each a Goro link that downloads it. See [Downloads](/api/downloads) | | `quoted_cost_micro`, `quoted_cost_usd` | integer, string | What was held before the run | | `cost_micro`, `cost_usd` | integer or null, string or null | What was actually charged. Null until the run settles | | `error` | object or null | `{code, message}` when something went wrong | | `created_at`, `started_at`, `finished_at` | string or null | ISO 8601 timestamps | | `price` | object | The tool's price, same shape as in [`discover`](/api/discover#price) | `cost_micro` is what left your balance. `quoted_cost_micro` is what was reserved while the run was in flight, and the difference between them was released back to you. ### 202: still running Past the synchronous window. You get a run id and poll it. ```json theme={null} { "run_id": "8c3a1f42-2d7b-4a15-9f0e-6b2c8d5e7a91", "status": "RUNNING", "status_url": "https://api.usegoro.ai/v1/runs/8c3a1f42-2d7b-4a15-9f0e-6b2c8d5e7a91", "quoted_cost_micro": 450000, "quoted_cost_usd": "$0.45", "price": { "type": "usage_based", "billed_on_actual_usage": true, "summary": "..." } } ``` Poll `status_url` until `status` is terminal. See [Runs](/api/runs#get-a-run) for what polling does and where the rows arrive. ### 502: the provider failed The tool failed or timed out inside the synchronous window. The hold has already been released in full and you are not charged. ```json theme={null} { "code": "provider_error", "message": "run failed: the provider did not complete this run", "run_id": "8c3a1f42-2d7b-4a15-9f0e-6b2c8d5e7a91", "status": "FAILED", "error": { "code": "provider_failure", "message": "The provider reported a failure." } } ``` `status` here is `FAILED` or `TIMED_OUT`. Retrying is safe and costs nothing for the attempt that failed. ## What a run costs Two numbers, doing two different jobs. The **hold** is taken before the tool starts and is sized from what you asked for. Request three rows and a small amount is held; request three hundred and more is held. The hold is not a charge, and it never exceeds the tool's ceiling. The **charge** is taken when the run settles, and how it is worked out depends on the tool's `price.type`: * `usage_based`, which is most of the catalog: worked out from what the call actually did, floored at `min_micro` and capped at both `max_micro` and the hold. Whatever is left of the hold is released. A run that returned nothing, or that cost nothing to serve, is billed nothing and comes back `free`. * `per_call`: `base_micro`, every time. The quote and the charge are the same number, so nothing is released. * `per_call_variant`, the nano banana image tools: the price of the option you asked for, picked from `variants` by the input named in `selector` (for those two, `resolution`). Still flat and still exact, so the quote and the charge are again the same number and nothing is released. Omit the field and you get `default_variant`'s price. * `metered`, the voice tools: the published rate times the size of the input, measured after the fact. Speech to text bills the real duration of the audio, which is usually shorter than the `max_duration_seconds` the hold was sized from, so the difference comes back. On a `usage_based` run Goro waits for the provider's own billing to settle before charging, which is why `cost_micro` is authoritative and `quoted_cost_micro` is not. Do not report the quote to a user as the price of the call. On a `per_call` run the two are equal by construction. ## Timing | Behaviour | Value | | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Synchronous wait before `202` | 25 seconds by default | | Run timeout | 5 minutes for most tools, 10 for image generation, which can legitimately take that long. Past it the run is stopped upstream, the hold is released, and the status is `TIMED_OUT` | ## Errors | Status | `code` | When | | ------ | ----------------------- | ------------------------------------------------------------------------------------------------ | | 400 | `validation_error` | `slug` missing, `input` not an object, or the input failed the tool's schema. Carries `errors` | | 401 | `unauthorized` | Missing or invalid API key | | 402 | `subscription_required` | The workspace has no active plan. Carries `billing_url` and `top_up_url`. Nothing is held | | 402 | `insufficient_funds` | The wallet cannot cover the hold. Carries `top_up_url`, `required_micro`, `plan` and `renews_at` | | 402 | `budget_exceeded` | A workspace budget cap would be breached. Carries `period`, `cap_micro`, `spent_micro` | | 404 | `not_found` | Unknown or inactive slug | | 502 | `provider_error` | The tool failed or timed out. Body as shown above | None of these charge you. A validation failure never reaches the provider and never places a hold; a failure after the hold releases it in full. Fetch, list and stop runs. # Runs Source: https://docs.usegoro.ai/api/runs List your runs, poll one to completion, or stop one that is still going. Three endpoints over the run record: list, fetch, stop. A run is yours or it does not exist, so a run id from another workspace answers `404`, never `403`. ## List runs ``` GET https://api.usegoro.ai/v1/runs ``` Newest first, cursor-paginated. | Parameter | Type | Notes | | --------- | ------- | ------------------------------------ | | `limit` | integer | Default 20, maximum 100 | | `cursor` | string | `next_cursor` from the previous page | ```bash theme={null} curl "https://api.usegoro.ai/v1/runs?limit=2" \ -H "Authorization: Bearer $GORO_API_KEY" ``` ```json theme={null} { "items": [ { "run_id": "8c3a1f42-2d7b-4a15-9f0e-6b2c8d5e7a91", "endpoint": "web.search", "status": "COMPLETED", "output": null, "output_count": 10, "quoted_cost_micro": 45000, "quoted_cost_usd": "$0.0450", "cost_micro": 10500, "cost_usd": "$0.0105", "error": null, "created_at": "2026-07-30T11:04:21.552Z", "started_at": "2026-07-30T11:04:21.998Z", "finished_at": "2026-07-30T11:04:29.140Z" } ], "next_cursor": "eyJjcmVhdGVkX2F0IjoiMjAyNi0wNy0zMFQxMTowNDoyMS41NTJaIiwiaWQiOiI4YzNhMWY0MiJ9" } ``` Items carry every field of the run record described under [`run`](/api/run#200-finished), except `price`. `output` is always `null` in this list. Goro does not store run results, and listing every run's rows would mean a provider round trip per row. Use `GET /v1/runs/{id}` to fetch the rows for a specific run. The list is a spend and status record: what ran, when, how many rows it made and what it cost. ## Get a run ``` GET https://api.usegoro.ai/v1/runs/{run_id} ``` Fetches a run, and advances it if it is still going. This is the polling endpoint behind a `202` from [`run`](/api/run). ```bash theme={null} curl https://api.usegoro.ai/v1/runs/8c3a1f42-2d7b-4a15-9f0e-6b2c8d5e7a91 \ -H "Authorization: Bearer $GORO_API_KEY" ``` The response is the run record, same shape as the list items above. Polling is safe to repeat as often as you like. Each call on a non-terminal run checks the provider once and settles the run if the provider is done: capturing the charge, releasing the rest of the hold, and recording the row count. Settlement is idempotent, so concurrent polls cannot double-charge. Stop polling once `status` is `COMPLETED`, `FAILED`, `STOPPED` or `TIMED_OUT`. ### Where the rows are The poll that finds the provider finished returns the rows in `output`. That is the response to keep. A finished run holds its rows until you collect them, so this works whether the run is still finishing or already settled: a poll that arrives after the run completed still returns its rows. That is the whole point of the async path. Once collected, or once the run is more than 24 hours old, the rows are deleted. Goro then falls back to re-reading the provider's own copy, which is best effort, so `output: null` on an older run is a normal outcome rather than an error. Write results somewhere yourself if you need them long term. ## Stop a run ``` POST https://api.usegoro.ai/v1/runs/{run_id}/stop ``` Aborts a run that is still going, releases the whole hold, and settles it as `STOPPED`. You are not charged. ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/runs/8c3a1f42-2d7b-4a15-9f0e-6b2c8d5e7a91/stop \ -H "Authorization: Bearer $GORO_API_KEY" ``` ```json theme={null} { "run_id": "8c3a1f42-2d7b-4a15-9f0e-6b2c8d5e7a91", "status": "STOPPED" } ``` The response is `202`. Stopping upstream is best effort; releasing your hold is not, and happens either way. ## Run statuses | Status | Terminal | Meaning | | ----------- | -------- | ----------------------------------------------------------------------------- | | `QUEUED` | no | Accepted, not yet handed to the provider | | `RUNNING` | no | In flight | | `COMPLETED` | yes | Finished. `cost_micro` is what you were charged | | `FAILED` | yes | The provider failed, or the run never started. Hold released, charged nothing | | `STOPPED` | yes | You stopped it. Hold released, charged nothing | | `TIMED_OUT` | yes | Exceeded the 5 minute run limit. Hold released, charged nothing | Every terminal status other than `COMPLETED` settles at a final cost of zero. ## Errors | Status | `code` | When | | ------ | ------------------ | ----------------------------------------------------------------------- | | 400 | `validation_error` | `limit` is not a positive integer, or the cursor is not one Goro issued | | 401 | `unauthorized` | Missing or invalid API key | | 404 | `not_found` | The run does not exist, or belongs to another workspace | | 409 | `conflict` | You tried to stop a run that has already finished | What is available, and where it went. # Uploads Source: https://docs.usegoro.ai/api/uploads POST /v1/uploads. Send a file to Goro out of band, then name it in the run with a short handle instead of its bytes. ``` POST https://api.usegoro.ai/v1/uploads ``` Mints an upload handle and a URL you PUT a file to. It costs nothing, spends nothing and writes no run row. One thing in the catalog takes a file today: the starting image a [video endpoint](/tools/video/video-grok) animates. Creating an upload still needs an active plan, the same as running a tool: without one this answers `402 subscription_required`, even though the call itself is free. See [Pricing](/pricing#plan-rules). ## Why the handle exists Tool arguments pass through a model's context window exactly like tool results do. Sending a photograph as `image_base64` therefore means the agent emits the whole file as tool-call arguments, and a 3 MB image is 4 MB of base64, on the order of a million tokens. What happens next is not theoretical: the payload is truncated, the run fails with something that reads like file corruption, and the agent downscales the user's photo and tries again with a smaller one. A handle is about thirty characters. The bytes go from you straight to storage, so they never pass through the model and never pass through Goro's API either. ## Two roads, and how to pick The response carries two ways to get the bytes in. They write the same object to the same path and produce the same handle, so nothing downstream can tell them apart. Which one is right depends on one question: can you actually reach the file? | | `upload_page_url` | `upload_url` | | ------------------------------------ | -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | | Who moves the bytes | The user, from their own browser | You, with a PUT | | Needs a shell | No | Yes | | Needs the file to be on your machine | No | Yes | | Good for | Phones, chat clients, anything sandboxed, and any case where the file is somewhere you cannot read | Scripts, CLIs, agents with a working shell and a local file | If you are an agent without a shell, send the user `upload_page_url` and wait for them to say it is done. Do not fall back to `image_base64`: a large inline payload is the failure this whole mechanism exists to prevent, and some clients refuse to render a tool call that size without reporting an error at all. ## The three steps ### 1. Ask for somewhere to put the file ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/uploads \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"content_type": "image/jpeg"}' ``` | Field | Type | Required | Notes | | -------------- | ------ | -------- | --------------------------------------------------------------------------------------- | | `content_type` | string | yes | The type of the file you are about to upload: `image/jpeg`, `image/png` or `image/webp` | The type you declare decides the object's extension and the type the store will serve it as. It is a claim rather than evidence, and it is treated as one: the file's own leading bytes are read when the handle is redeemed, and a pair that disagrees is refused. ```json theme={null} { "handle": "upl_9Qw3mJf2Lk7Rn0Bd4Ts1Vy.jpg", "upload_page_url": "https://upload.usegoro.ai/eyJlIjoxNzg1...Kd2Qh8Lm", "upload_url": "https:///object/upload/sign/input-image/0f1e2d3c/9Qw3mJf2Lk7Rn0Bd4Ts1Vy.jpg?token=eyJhbGciOi...", "upload_method": "PUT", "upload_headers": { "Content-Type": "image/jpeg" }, "max_bytes": 10485760, "accepted_content_types": ["image/jpeg", "image/png", "image/webp"], "usable_for_seconds": 600, "page_usable_for_seconds": 3600, "deleted_after_seconds": 1800, "next": { "upload_yourself": "curl -X PUT --data-binary @ -H \"Content-Type: image/jpeg\" \"\"", "or_ask_the_user": "Send them upload_page_url and wait until they say it is uploaded.", "then": "Send image_handle: \"upl_9Qw3mJf2Lk7Rn0Bd4Ts1Vy.jpg\" in the run input. Do not send image_base64 as well." }, "note": "Upload the file, then run within 10 minutes. The file is private (no public read, no unsigned address that works) and is deleted 30 minutes after it lands, so get the handle immediately before the run rather than in advance." } ``` | Field | Type | Notes | | ------------------------- | -------------- | -------------------------------------------------------------------------------------------------------- | | `handle` | string | What you pass as `image_handle` in the run input | | `upload_page_url` | string | A page a person opens to pick the file. Needs no shell, no Goro account and no sign-in | | `upload_url` | string | Where to PUT the file yourself. Signed, single use, and it addresses the storage bucket rather than Goro | | `upload_method` | string | `PUT` | | `upload_headers` | object | The headers to send with the PUT. Today only `Content-Type`, set to the type you declared | | `max_bytes` | integer | 10485760, ten megabytes | | `accepted_content_types` | array | The three types `content_type` accepts | | `usable_for_seconds` | integer | 600. How long after the upload finishes the handle can still be redeemed | | `page_usable_for_seconds` | integer | 3600. How long `upload_page_url` stays openable | | `deleted_after_seconds` | integer | 1800. How long after the file lands it is deleted | | `next`, `note` | object, string | The next command and the rule in a sentence, for a model reading the response | Values are illustrative. Treat `upload_url` as opaque: it addresses the storage provider rather than `api.usegoro.ai`, and it carries its own credential in the query string. Both `upload_page_url` and `upload_url` are capability URLs. Anyone holding one can write that one file to that one path until it expires, so treat them the way you would treat any signed link: hand them to the person they were minted for and nobody else. ### 2. Either PUT the file, or send the user the page If you have a shell and the file is on this machine: ```bash theme={null} curl -X PUT --data-binary @photo.jpg \ -H "Content-Type: image/jpeg" \ "$UPLOAD_URL" ``` Send no `Authorization` header with it. The URL is the credential, it is good for one write to one path, and it points at the private bucket directly. That is the whole point of the two requests: the file never enters a Goro serverless function, never meets a request body limit, and never enters the model. Otherwise, give the user `upload_page_url`: > Open this link and choose the photo: `https://upload.usegoro.ai/…` > Tell me when it's uploaded. They get a one-field page that checks the file is really the type you asked for and sends it from their browser straight to the same bucket. Nothing is installed, nothing is signed into, and the bytes never travel through you. Wait for them to confirm before step 3: the handle does not resolve until the file has actually landed. The page checks the file's leading bytes before sending, so a user who picks an iPhone HEIC when you asked for `image/jpeg` is told immediately, on the page, instead of failing minutes later inside a paid run. ### 3. Run the tool with the handle ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "slug": "video.grok", "input": { "prompt": "The camera pushes in slowly as the steam rises and the light shifts towards evening.", "image_handle": "upl_9Qw3mJf2Lk7Rn0Bd4Ts1Vy.jpg" } }' ``` A handle is `upl_`, then 22 base64url characters, then `.jpg`, `.png` or `.webp`. Nothing else parses, because the handle becomes part of a storage path. It carries the object id and the extension and nothing else: the workspace comes from the key you authenticated with, so another workspace's handle looks in your own prefix for an object that is not there. ## Three ways to send a starting image Exactly one of these per call. Sending two is refused, because picking a winner would mean animating an image you did not mean and charging you for it. | Field | Use it when | Ceiling | | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | `image_handle` | Any file held locally or privately, whether you PUT it yourself or the user uploads it through the page. This is the road most callers should take | 10 MB | | `image_urls` | The image is already on a public https link, including the output URL of an image endpoint | 10 MB | | `image_base64` | The image is genuinely small AND neither road above is available | 3 MB | `image_base64` is not going away, but "I have no shell" stopped being a reason to use it: that is what `upload_page_url` is for. It remains the right answer for a small file when there is no user to ask either. It is the wrong answer for a photograph, and the failure it produces is silent enough to be worth naming twice: a truncated base64 string decodes to fewer, wrong bytes rather than to an error, and some clients will not even render a tool call that large, which looks like the request vanishing. ## Why the ceilings differ The handle path and the link path both stop at 10 MB, which is kie.ai's own per-image limit, so Goro rejects exactly what the model would reject and nothing more. On the upload path that limit is also declared on the bucket, so an oversized file is refused by the storage layer before a byte of it is stored. The inline path stops at 3 MB for a reason that has nothing to do with images. Base64 inflates by exactly 4/3, so 3 MB of image is 4 MB of request body, and the serverless request body limit behind this API is 4.5 MB. That ceiling is a property of the transport and cannot be raised. An upload does not go through that transport at all, which is the second reason it exists. ## The two clocks | Clock | Value | What it governs | | ---------- | ---------- | ------------------------------------------------------------------------ | | Page | 60 minutes | How long `upload_page_url` can still be opened | | Redemption | 10 minutes | How long after the upload finishes the handle can still be used in a run | | Retention | 30 minutes | How long the file exists at all, counted from when it lands | The page clock extends nothing. Retention is measured from the moment the bytes land, so a link opened at minute 59 produces a file deleted 30 minutes after that, exactly like one opened immediately. What the hour buys is the gap between an agent posting a link and a person picking up their phone. The second number did not change when uploads were added. The ten minutes are subtracted from the thirty rather than added to them: the signed link handed to the generative provider is worth the twenty that remain, so a link can never outlive the object it points at, and no image lives past thirty minutes. The file is deleted by a scheduled sweep that reads the bucket by age every ten minutes, which is the same job, the same bucket and the same clock the inline intake already used. The practical rule is one line: get the handle immediately before the run, not in advance. A handle from an earlier session is expected to be gone. ## What is stored, and what is not Nothing about the upload path is more exposed than the inline one, and in one respect it is less: the bytes reach storage without passing through our API. * The bucket is private. There is no public read and no unsigned address that works. * The object's name is random and sits under a path scoped to your workspace. Nothing about it is derivable from the run record, deliberately, which is why the deletion sweep reads the bucket rather than working backwards from a run. * The only link that exists is signed and time limited, and it is handed to the generative provider, not published. * An unredeemed handle leaves nothing behind. The object is swept at thirty minutes like every other one. The [Privacy Policy](https://usegoro.ai/legal/privacy) section 4.16 is the authoritative version of this, thirty minutes included. That number is a published claim rather than an implementation detail, which is why the upload path was fitted inside it. ## Errors Minting a handle can fail in two ways. | Status | `code` | When | | ------ | ----------------------- | ------------------------------------------------------------------------ | | 400 | `validation_error` | `content_type` is missing, or is not one of the three accepted types | | 401 | `unauthorized` | Missing or invalid API key | | 402 | `subscription_required` | The workspace has no active plan. Carries `billing_url` and `top_up_url` | Everything else surfaces on the run that redeems the handle, and all of it is checked before the hold is placed, so none of it charges you. | Status | `code` | When | What to do | | ------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 404 | `not_found` | The handle is unknown, belongs to another workspace, or the file has already been swept | Mint a fresh handle, upload, and run straight after. The three cases answer identically on purpose, so the error cannot be used to learn which handles exist | | 400 | `validation_error` | The file is not a JPEG, PNG or WEBP. Decided by reading the file's own leading bytes, so a correct `Content-Type` on something that is not an image does not get past it | Convert the file and upload it again | | 400 | `validation_error` | The file's real type disagrees with the `content_type` the handle was created for | The store serves the object as the type you declared, and a model given the wrong one fails the generation. Call `POST /v1/uploads` again with the type the file really is, and upload to that handle | | 400 | `validation_error` | The uploaded file is over 10 MB. The bucket refuses an oversized write in the first place, so this is the backstop rather than the usual answer | Compress or resize the file, upload it again, and use the handle from that upload | | 400 | `validation_error` | More than one of `image_handle`, `image_urls` and `image_base64` is present | Send exactly one | | 400 | `validation_error` | `image_base64` is not valid base64, or is over its limit | Almost always truncation rather than corruption, because the string passed through a context window. Do not shrink the image. Upload it and send the handle | A handle that does not resolve is settled before the run is quoted, so a bad handle costs nothing: no hold, no run row, nothing to release. ## From MCP The MCP server exposes the same endpoint as `create_upload`. It takes `content_type` and returns the same handle, the same page link and the same upload URL, with the exact `curl` to run next. It needs the `tools:read` scope rather than `tools:run`, because it spends nothing. The instruction for an agent is short enough to repeat: never read a file into the conversation in order to send it. Call `create_upload`. If you have a shell and the file is on that machine, PUT it. Otherwise give the user `upload_page_url` and wait for them. Either way, pass the handle to `run_tool`. Do not resize the image to make it fit, because fitting is not the problem the size was causing. ## From the CLI `goro run` takes `--image` and does all three steps for you. ```bash theme={null} goro run -s video.grok \ -i '{"prompt":"The camera pushes in slowly as the steam rises."}' \ --image ./photo.jpg ``` The CLI reads the file's leading bytes to work out what it really is, asks for an upload URL for that type, PUTs the bytes and drops the handle into the input. You never spell `image_handle`, and a `.jpg` that is really a PNG still works. See [`goro run`](/cli/run#sending-a-local-image). What a run costs, what it returns, and how the hold settles. # Wallet Source: https://docs.usegoro.ai/api/wallet GET /v1/wallet/balance and GET /v1/wallet/activities. What is available to spend, and the ledger that explains it. ## Balance ``` GET https://api.usegoro.ai/v1/wallet/balance ``` ```bash theme={null} curl https://api.usegoro.ai/v1/wallet/balance \ -H "Authorization: Bearer $GORO_API_KEY" ``` ```json theme={null} { "plan": "build", "balance_micro": 4250000, "promo_micro": 18400000, "included_remaining_micro": 18400000, "available_micro": 22650000, "balance_usd": "$4.25", "promo_usd": "$18.40", "included_remaining_usd": "$18.40", "available_usd": "$22.65", "renews_at": "2026-10-02T09:14:07.000Z" } ``` | Field | Type | Notes | | ---------------------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `plan` | string | `free`, `build` or `scale`. `free` means the workspace has never subscribed, or its plan has ended | | `balance_micro`, `balance_usd` | integer, string | Cash you topped up. Never expires | | `promo_micro`, `promo_usd` | integer, string | Credit that expires: included plan credit while you are on a plan, or promotional credit Goro has granted by hand otherwise. Reads zero once expired | | `included_remaining_micro`, `included_remaining_usd` | integer, string | The unexpired included credit from the current plan period. Zero on a workspace with no plan | | `available_micro`, `available_usd` | integer, string | Cash plus unexpired included or promo credit. What you can spend right now | | `renews_at` | string or null | ISO 8601. When the plan renews and a fresh cycle of included credit lands. `null` on the Free plan | | `plan_name` | string | `Free`, `Build` or `Scale`, for display | | `plan_ending` | boolean | `true` when the plan is cancelled and `renews_at` is the date it ends instead of renews | | `top_up_url`, `billing_url` | string | The same address: the Billing page, where a plan or one-time credit can be added. `top_up_url` is the historical name and stays | `available_micro` is the number to check before a run. Anything currently held by an in-flight run has already left `balance_micro`, so the available figure never double-counts a run you have not settled yet. **A workspace with `plan: "free"` cannot run tools.** It keeps its wallet, including any cash or promotional credit sitting on it, but `POST /v1/run` and `POST /v1/uploads` answer `402 subscription_required` until it subscribes. Reading the balance, `discover` and `inspect` all work regardless of plan. Promotional credit is spent before cash, and it expires. Goro grants it by hand, for support or goodwill, rather than automatically at signup. An expired promo drops out of the balance automatically. Promotional credit and included plan credit share the same expiring bucket: on a plan, `included_remaining_micro` is the period's included credit, it is spent before cash in the same way, and it expires at the end of the period rather than rolling over. The next cycle lands at `renews_at`. This is also the fastest check an agent can make after a `402 insufficient_funds`: it tells you exactly how much is there so you can tell a human what to top up, or when the plan's included credit comes back. ## Activities ``` GET https://api.usegoro.ai/v1/wallet/activities ``` The append-only ledger, newest first. Every movement of money is a row here, and nothing is ever edited or deleted. | Parameter | Type | Notes | | --------- | ------- | ------------------------------------ | | `limit` | integer | Default 20, maximum 100 | | `cursor` | string | `next_cursor` from the previous page | ```bash theme={null} curl "https://api.usegoro.ai/v1/wallet/activities?limit=3" \ -H "Authorization: Bearer $GORO_API_KEY" ``` ```json theme={null} { "items": [ { "id": 4417, "kind": "hold_release", "amount_micro": 34500, "amount_usd": "$0.0345", "balance_after_micro": 4250000, "balance_after_usd": "$4.25", "run_id": "8c3a1f42-2d7b-4a15-9f0e-6b2c8d5e7a91", "stripe_ref": null, "note": null, "created_at": "2026-07-30T11:04:29.150Z" }, { "id": 4416, "kind": "debit_capture", "amount_micro": 10500, "amount_usd": "$0.0105", "balance_after_micro": 4215500, "balance_after_usd": "$4.2155", "run_id": "8c3a1f42-2d7b-4a15-9f0e-6b2c8d5e7a91", "stripe_ref": null, "note": null, "created_at": "2026-07-30T11:04:29.148Z" } ], "next_cursor": "eyJpZCI6NDQxNn0" } ``` | Field | Type | Notes | | ------------------------------------------ | --------------- | ---------------------------------------------------------------------------------------- | | `id` | integer | Ledger sequence number. Higher is newer | | `kind` | string | See the table below | | `amount_micro`, `amount_usd` | integer, string | The size of this entry, unsigned | | `balance_after_micro`, `balance_after_usd` | integer, string | Wallet balance after this entry | | `run_id` | string or null | The run this entry belongs to, for run entries | | `stripe_ref` | string or null | The Stripe reference: the payment for a top-up or refund, the invoice id for plan credit | | `note` | string or null | Free text, when there is any | | `created_at` | string | ISO 8601 | ### Entry kinds | `kind` | What it means | | --------------- | --------------------------------------------------------------------------------------- | | `credit` | A top-up landed | | `promo` | Promotional credit was granted | | `plan_credit` | One billing cycle of included plan credit landed. `stripe_ref` is the Stripe invoice id | | `debit_hold` | A run reserved funds. The full amount leaves the balance here | | `debit_capture` | A run was charged. Records what the run actually cost | | `hold_release` | The unused part of a hold came back | | `refund` | Money was returned to you | Do not sum these as signed amounts. `debit_hold` is what leaves the balance when a run starts; `debit_capture` records the charge but does not itself move the balance again, and `hold_release` returns the unused remainder. Treating hold and capture as two separate debits double-counts every run. Read `balance_after_micro` on each row instead of deriving a running total. A completed run leaves three rows: one `debit_hold`, one `debit_capture` for the actual cost, and one `hold_release` for the difference. A run that failed, timed out or was stopped leaves a `debit_hold` and a `hold_release` for the whole amount, and no capture. ## Errors | Status | `code` | When | | ------ | ------------------ | ----------------------------------------------------------------------- | | 400 | `validation_error` | `limit` is not a positive integer, or the cursor is not one Goro issued | | 401 | `unauthorized` | Missing or invalid API key | Rates, totals and ceilings, in plain terms. # goro discover Source: https://docs.usegoro.ai/cli/discover Find the tools that can answer a plain-English question, with the price of each. ```bash theme={null} goro discover -q "find recent tweets about ai agents" ``` You do not need to know the catalog. Describe what you want and `discover` ranks the tools against it. ``` 5 matches for "find recent tweets about ai agents" SLUG NAME PRICE WHEN TO USE youtube.videos YouTube videos $0.0090 per result Use to find videos on a topic, pull a channel... twitter.followers X followers and fol... $0.000450 per result Use to map who follows an X account, to size ... twitter.search Twitter search $0.0012 per result Use when you need tweets about a topic, senti... amazon.product Amazon product details $0.0045 per result Use when you already have ASINs or Amazon pro... amazon.search Amazon search results $0.0003 per result Use to find products on Amazon by keyword, to... Prices shown as a ceiling are billed on actual usage. You are charged what the call cost, which is usually well under the ceiling. Next: goro inspect -s youtube.videos ``` ## Options | Option | Meaning | | --------------- | ------------------------------------------------ | | `-q`, `--query` | What you want to do, in plain English. Required. | | `--limit ` | How many matches to return, 1 to 20. Default 5. | Plus the [global options](/cli/overview#global-options). ## Reading the price column The `PRICE` column is the **rate**: what one unit costs you, printed flat because it is exact. A row, a page, a thousand bytes of text, a second of audio, or the whole call on a flat-priced image tool. Two of those are not the total. On a scrape the total follows the rows the call really produced, and on a voice call it follows the size of what you sent. The words "up to" appear for a ceiling and for nothing else, so a number without them is never a maximum. A rate is not a total. A `web.search` call at $0.0075 per page charges around a cent for a normal request, well under the $1.00 ceiling `inspect` shows. An image call is the other extreme: one exact price, nothing to settle. See [Pricing](/pricing). ## JSON output ```bash theme={null} goro discover -q "scrape a google maps place" --limit 2 -j ``` ```json theme={null} { "items": [ { "slug": "maps.places", "name": "Google Maps places", "category": "maps", "when_to_use": "Use when you need local businesses or points of interest...", "price": { "type": "usage_based", "min_micro": 2000, "min_usd": "$0.0020", "max_micro": 6000000, "max_usd": "$6.00", "billed_on_actual_usage": true, "per_unit_micro": 9000, "per_unit_usd": "$0.0090", "unit_label": "place", "summary": "$0.0090 per place, billed on actual usage (minimum $0.0020 per call), up to $6.00 per call" }, "sample_input": { "language": "en", "scrapeContacts": false } } ] } ``` `per_unit_usd` and `unit_label` are the rate the table prints. `billed_on_actual_usage: true` says the total still depends on the call, so `max_usd` next to it is a maximum. On a flat-priced tool that flag is absent and `base_usd` is the whole price. An agent can tell the two apart without reading English. `sample_input` is fillable, so you can go straight to `run` without an `inspect` round trip. ## No matches ``` No endpoints matched "sequence a genome". Try different words, or a broader description. ``` Exit code is still `0`. An empty catalog match is an answer, not an error. Read the exact input a tool takes before you call it. # Examples Source: https://docs.usegoro.ai/cli/examples Recipes: piping to jq, scripting on exit codes, batching inputs, and running against a local gateway. ## From question to rows in three commands ```bash theme={null} goro discover -q "reviews for a restaurant on google maps" goro inspect -s maps.reviews goro run -s maps.reviews -i '{"startUrls":[{"url":"https://maps.app.goo.gl/xyz"}],"maxReviews":20}' -o reviews.json ``` ## Pipe straight into jq `-j` prints the raw response, so the whole result is one JSON document. ```bash theme={null} goro run -s web.search -i '{"queries":"best espresso machines 2026"}' -j \ | jq -r '.output[0].organicResults[] | "\(.position) \(.title) \(.url)"' ``` ``` 1 Best Espresso Machines for Coffee Nerds & Beginners ... https://www.bonappetit.com/story/best-espresso-machines 2 Best Espresso Machine 2026? https://www.reddit.com/r/espresso/comments/... 3 The Best Espresso Machine for Home in 2026 - Cafe Bueno https://shopcafebueno.com/blogs/... ``` Pull just the slugs and prices out of a discover. Use `price.summary` rather than `max_usd`: a flat-priced tool has no ceiling, so `max_usd` on one of those is `null`. ```bash theme={null} goro discover -q "linkedin company employees" -j \ | jq -r '.items[] | "\(.slug)\t\(.price.summary)"' ``` ## Branch on the exit code ```bash theme={null} #!/bin/bash set -u goro run -s web.search -i '{"queries":"ai agent frameworks"}' -o out.json case $? in 0) jq '.output_count' out.json ;; 1) echo "bad request, not retrying" ; exit 1 ;; 2) echo "key rejected, check GORO_API_KEY" ; exit 1 ;; 3) echo "wallet empty, topping up needed" ; exit 1 ;; 4) echo "upstream failed, retrying once in 60s" sleep 60 goro run -s web.search -i '{"queries":"ai agent frameworks"}' -o out.json ;; esac ``` Exit `3` is the one worth handling separately in a long-running loop: it means the work is fine and the money ran out, so retrying immediately just burns requests. ## Stop before you run out ```bash theme={null} #!/bin/bash # Halt the batch when the balance drops under a dollar. floor=1000000 # micro-USD while read -r query; do available=$(goro balance -j | jq '.available_micro') if [ "$available" -lt "$floor" ]; then echo "balance $available below floor, stopping" break fi goro run -s web.search -i "$(jq -n --arg q "$query" '{queries:$q}')" \ -o "results/$(echo "$query" | tr ' /' '__').json" done < queries.txt ``` ## Batch a file of inputs ```bash theme={null} # inputs.jsonl, one JSON object per line while read -r line; do echo "$line" | goro run -s instagram.profile -f - -j >> out.jsonl done < inputs.jsonl ``` `-f -` reads the input from stdin, so anything that can generate JSON can drive the CLI. ## Long-running calls ```bash theme={null} # Kick it off and come back later. run_id=$(goro run -s maps.places -f big-query.json -j | jq -r '.run_id') # ... other work ... goro runs get -r "$run_id" --wait --timeout 900 -o places.json ``` ## Animate a local photo ```bash theme={null} goro run -s video.grok \ -i '{"prompt":"The camera pulls back slowly as the neon sign flickers.","duration":6}' \ --image ./storefront.jpg --wait -o clip.json curl -sL "$(jq -r '.output[0]' clip.json)" -o clip.mp4 ``` `--image` uploads the file straight to storage and passes the handle, so the picture never has to be published anywhere and never has to be base64. The run answers with a link to the MP4 rather than the file, and that link is the model provider's and temporary, which is why the download is in the same script. ## Two workspaces on one machine ```bash theme={null} GORO_CONFIG_HOME=~/.goro-client-a goro setup GORO_CONFIG_HOME=~/.goro-client-b goro setup alias goro-a='GORO_CONFIG_HOME=~/.goro-client-a goro' alias goro-b='GORO_CONFIG_HOME=~/.goro-client-b goro' goro-a balance goro-b balance ``` Or keep one config and switch: ```bash theme={null} goro keys use client-a ``` ## CI No `setup`, no config file, nothing written to disk: ```yaml theme={null} - name: Fetch competitor prices env: GORO_API_KEY: ${{ secrets.GORO_API_KEY }} run: | npx goro run -s amazon.search \ -i '{"searchTerms":["espresso machine"],"maxItems":20}' \ -o prices.json ``` ## Against a local gateway ```bash theme={null} goro discover -q "web search" --api http://localhost:3030 ``` Pass `--api` to `setup` and it is remembered in your config, so you only need it once per profile. ## What a call actually cost ```bash theme={null} goro runs list -n 5 -j | jq -r '.items[] | "\(.endpoint)\t\(.cost_usd)\t\(.output_count) rows"' ``` ``` web.search $0.0105 1 rows web.search $0.0105 1 rows maps.places $0.1420 20 rows ``` The quoted ceiling for those `web.search` calls was \$1.00. What they cost was about a cent, because billing follows what the call actually consumed. See [Pricing](/pricing). # goro inspect Source: https://docs.usegoro.ai/cli/inspect Read a tool's real input schema, its price and a sample input you can send as-is. ```bash theme={null} goro inspect -s web.search ``` ``` web.search Web search category search about Run a Google web search and get back ranked organic results with title, url, and snippet. use when Use when you need current information from the web, to find pages about a topic, or to answer questions that need fresh sources. Billed per result page, about 10 results each. price $0.0075 per page up to $1.00 for a single call, and never more than that. Billed on actual usage, minimum $0.0020. The hold scales with what you request. You are charged for what the call actually did, and nothing if it cost nothing. Input FIELD TYPE REQ NOTES site string Limit to one site, e.g. example.com queries string yes Search term(s), or Google Search URLs. Newline-separate for... countryCode string Two-letter country code, sets the Google domain (e.g. es fo... languageCode string Interface language (hl) mobileResults boolean default false searchLanguage string Restrict results to this language (lr) forceExactMatch boolean default false maxPagesPerQuery integer Result pages per query, roughly 10 results each. Billing is... Input is forwarded to the provider unchanged, so any field it accepts works. Run it goro run -s web.search -i '{"queries":"","mobileResults":false,"forceExactMatch":false,"maxPagesPerQuery":1}' ``` ## Options | Option | Meaning | | -------------- | -------------------------------------------------- | | `-s`, `--slug` | The tool slug, for example `web.search`. Required. | Plus the [global options](/cli/overview#global-options). ## The input table is the real schema On the passthrough tools, which is most of the catalog, Goro forwards your input to the underlying tool unchanged and hands back its rows untouched. The fields listed are the fields the tool itself accepts, not a translation, so anything the tool takes works even if it is not in the table. The line under the table says as much. The image and voice tools are wrapped rather than passed through, and their table is the complete list. A field that is not in it is rejected instead of forwarded. Fields that cost extra are called out in the tool's notes. Turning on a paid extra raises what the call actually consumes, and therefore what you are charged, but never above the ceiling. ## The price block On a scrape or a voice call the first line is the rate, stated flat because it is exact: what one row, one page, one thousand bytes or one second costs you. Under it come the ceiling for a single call, always written as "up to", the fact that billing follows actual usage with a floor, and the note that the hold is sized from your specific request rather than from the ceiling. A flat-priced image tool prints a single line, `$0.18 per call, flat.`, because there is nothing else to say about it. An unknown slug exits `1`: ``` $ goro inspect -s nope.nope Error unknown endpoint: nope.nope ``` ## Copy the run line The last block is a ready-to-run command with the sample input filled in. Replace the placeholders and go: ```bash theme={null} goro run -s web.search -i '{"queries":"best espresso machines 2026","maxPagesPerQuery":1}' ``` Call the tool and see what it actually cost. # goro keys Source: https://docs.usegoro.ai/cli/keys Store, list and switch API keys, without a secret ever reaching your screen or your shell history. ## setup ```bash theme={null} goro setup ``` Prompts for a key, verifies it against the gateway, and stores it. Verification happens before storage, so a mistyped key fails here rather than on your first real call. ``` Key verified and stored. name default (active) key goro_live_mz...WNE0 workspace b818a36d-bfb6-4111-a0aa-ce14cfbbb34e gateway https://api.usegoro.ai config /Users/you/.goro/config.json (mode 0600) Next: goro balance ``` | Option | Meaning | | --------------- | ------------------------------------------------------- | | `--name ` | Store under a name of your choosing. Default `default`. | | `--key ` | Provide the key non-interactively | `--key` puts a live secret in your shell history. Prefer the prompt, or pipe it in: `echo "$GORO_KEY" | goro setup`. For CI, set `GORO_API_KEY` and skip `setup` entirely. ## keys add ```bash theme={null} goro keys add --name work ``` Same flow, but it does not switch the active key unless it is the first one stored. Useful when you have a personal workspace and a client workspace. ## keys list ```bash theme={null} goro keys list ``` ``` Stored keys (/Users/you/.goro/config.json) NAME KEY WORKSPACE ADDED * default goro_live_mz...WNE0 b818a36d-bfb6-4111-a0aa-ce14cfbbb34e 2026-07-31 work goro_live_Zz...0fD9 66666666-7777-8888-9999-000000000000 2026-07-31 ``` `*` marks the active key. Keys always print as a prefix plus the last four characters. The full secret is never printed by any command, in either human or JSON output. If `GORO_API_KEY` is set in your shell, `keys list` says so, because that key wins over everything stored: ``` GORO_API_KEY is set and overrides all of these: goro_live_Zz...0fD9 ``` ## keys use ```bash theme={null} goro keys use work ``` ``` Active key: work goro_live_Zz...0fD9 ``` ## Where keys live `~/.goro/config.json`, mode `0600`, inside a directory with mode `0700`. The CLI re-applies those permissions on every write, so a file that something else loosened is tightened again rather than left open. If it finds the file world-readable, `keys list` warns: ``` Warning: /Users/you/.goro/config.json is mode 0644, expected 0600. Run `chmod 600 /Users/you/.goro/config.json`. ``` Point the whole config directory somewhere else with `GORO_CONFIG_HOME`, which is how you keep separate profiles: ```bash theme={null} GORO_CONFIG_HOME=~/.goro-client-a goro setup ``` ## Precedence When more than one key is available, the first match wins: An environment variable beats everything stored. Nothing is written to disk. Whatever `keys use` last selected. The gateway base URL resolves the same way: `--api`, then `GORO_API_URL`, then the `api_url` saved in your config, then the default. ## Losing a key Revoke it in the dashboard. A revoked key fails closed on the next call: ``` $ goro balance Error Unknown or revoked API key ``` Exit code `2`, so a script can tell an auth problem from an empty wallet (`3`) or a flaky provider (`4`). # CLI Source: https://docs.usegoro.ai/cli/overview Drive the gateway from a terminal or a script: discover a tool, inspect it, run it, and see what it cost. The `goro` command line client wraps the same HTTP API your agent uses. It is useful for three things: setting up a key once, exploring the catalog by hand before you wire anything up, and scripting calls where a full agent would be overkill. Human-readable by default, raw JSON on demand, and an exit code that says what happened so a shell script can branch on it. ## Install ```bash theme={null} npm install -g @goroo/cli ``` That installs a `goro` command. The package is scoped because the bare `goro` name on npm belongs to an unrelated project. Prefer not to install globally? `npx @goroo/cli discover -q "..."` works the same way. ## First run ```bash theme={null} goro setup ``` It prompts for an API key, verifies it against the gateway, and stores it in `~/.goro/config.json` with permissions `0600`. The key is never echoed while you paste it and is never printed back afterwards. ``` Key verified and stored. name default (active) key goro_live_mz...WNE0 workspace b818a36d-bfb6-4111-a0aa-ce14cfbbb34e gateway https://api.usegoro.ai config /Users/you/.goro/config.json (mode 0600) Next: goro balance ``` ## The commands | Command | What it does | | -------------------------------- | ------------------------------------------- | | `goro setup` | Store and verify your first API key | | `goro keys add` | Store an additional key | | `goro keys list` | Show stored keys, prefix and last 4 only | | `goro keys use ` | Switch the active key | | `goro discover -q ""` | Find tools that can answer a question | | `goro inspect -s ` | Show a tool's input, price and sample input | | `goro run -s -i ''` | Run a tool | | `goro runs get -r ` | Fetch one run | | `goro runs list` | List recent runs | | `goro balance` | Show the wallet balance | ## Global options | Option | Meaning | | ----------------- | ----------------------------------------------------- | | `-j`, `--json` | Emit the raw JSON response instead of a rendered view | | `-o ` | Write the JSON response to a file | | `--api ` | Point at a different gateway, for example a local one | | `-h`, `--help` | Help for the CLI or for one command | | `-V`, `--version` | Print the CLI version | `-j` and `-o` are independent. `-o` always writes the same JSON `-j` prints, so you can keep a readable terminal and still capture the machine-readable result: ```bash theme={null} goro run -s web.search -i '{"queries":"cold brew ratio"}' -o results.json ``` ## Exit codes Every command exits with one of five codes. Scripts branch on these rather than parsing output. | Code | Meaning | What to do | | ---- | ---------------------------------- | -------------------------------------------- | | `0` | Success | Continue | | `1` | Usage error | Fix the command, the slug or the input JSON | | `2` | Auth failure | The key is missing, wrong or revoked | | `3` | No active plan, insufficient funds | Pick a plan, top up, or raise the budget cap | | `4` | Upstream failure | The provider or gateway failed, retry later | ```bash theme={null} goro run -s web.search -i "$INPUT" -o out.json case $? in 0) echo "got results" ;; 3) echo "out of money, stopping" ; exit 1 ;; 4) echo "upstream flaked, retrying in 60s" ; sleep 60 ;; esac ``` A run that comes back `FAILED` or `TIMED_OUT` also exits `4`, even though the HTTP call itself succeeded. The run is what you asked about, so the run is what the exit code reports on. ## How prices read The CLI shows two different kinds of number and never mixes them up. A rate is what one unit costs you: a row, a page, a second of audio. It prints flat, with no hedging: `$0.0069 per profile`. A flat-priced tool prints the same way, and there the rate is the whole price: `$0.18 per call`. Before a call runs, the price attached to a tool is a ceiling for your request, not a prediction. It always prints with the words "up to", for example `up to $6.00 per call`. You are never charged above it, and the final charge is usually well below it. After a call completes, the CLI shows what it actually cost and what that works out to per result: `charged $0.0105 (1 result, $0.0105 each)`. See [Pricing](/pricing) for how the hold and the final charge are computed. ## Environment | Variable | Effect | | ------------------ | ------------------------------------------------------- | | `GORO_API_KEY` | Use this key, overriding anything stored | | `GORO_API_URL` | Gateway base URL, below `--api` and above stored config | | `GORO_CONFIG_HOME` | Config directory, default `~/.goro` | `GORO_API_KEY` is the one to reach for in CI: nothing is written to disk, and no `setup` step is needed. ```bash theme={null} GORO_API_KEY=$GORO_KEY goro run -s web.search -i '{"queries":"ai agents"}' -j ``` Piping to jq, scripting retries, and running a batch of inputs. # goro run Source: https://docs.usegoro.ai/cli/run Run a tool, with the input inline or from a file, synchronously or by polling. ```bash theme={null} goro run -s web.search -i '{"queries":"best espresso machines 2026","maxPagesPerQuery":1}' ``` A fast call returns the rows straight away: ``` COMPLETED web.search d5f6185e-7faa-4fd3-b81b-0d02f4f9e918 cost charged $0.0105 (1 result, $0.0105 each) held $0.0210, the rest was released finished 2026-07-31T09:25:44.927+00:00 Output (1 row) { "searchQuery": { "term": "best espresso machines 2026", "page": 1, ... }, "organicResults": [ ... ] } ``` The `cost` line is what you were actually charged, and what that works out to per result. The `held` line is what was reserved before the call ran. The difference was released back to your balance. ## Options | Option | Meaning | | --------------------- | ----------------------------------------------------------------------- | | `-s`, `--slug` | The tool slug. Required. | | `-i`, `--input` | Input as a JSON object | | `-f`, `--file` | Read the input JSON from a file, or `-` for stdin | | `--image ` | Upload a local image and pass it as `image_handle`. For the video tools | | `--wait` | Poll until the run finishes | | `--timeout ` | How long `--wait` waits. Default 300. | Plus the [global options](/cli/overview#global-options). `-i` and `-f` are mutually exclusive, and passing both is a usage error before anything is sent. ## Input from a file or stdin Long or generated inputs are easier from a file: ```bash theme={null} goro run -s maps.places -f query.json ``` ```bash theme={null} jq -n '{searchStringsArray:["dentist in Cluj"],maxCrawledPlacesPerSearch:20}' \ | goro run -s maps.places -f - ``` ## Sending a local image The video tools animate a starting image. `--image` is the whole file intake in one flag: ```bash theme={null} goro run -s video.grok \ -i '{"prompt":"The camera pushes in slowly as the steam rises."}' \ --image ./photo.jpg ``` The CLI reads the file's leading bytes to work out what it really is, asks the gateway for a one-shot upload URL, PUTs the bytes straight to storage and puts the handle in your input. Nothing about encoding, handles or content types reaches you, and a `.jpg` that is really a PNG still works. JPEG, PNG and WEBP, up to 10 MB. The upload happens before the run, so a file the gateway will not take costs nothing: no hold, no run row. `--image` supplies the image on its own, so setting `image_handle`, `image_urls` or `image_base64` in the input as well is a usage error before anything is sent. The two requests behind that one flag exist for agents rather than for you. Tool arguments pass through a model's context window, so an image sent inline as base64 costs a million tokens and gets truncated. [Uploads](/api/uploads) explains the flow and the 10 and 30 minute clocks on an uploaded file. ## Slow calls Some calls take minutes. If a run does not finish inside the gateway's synchronous window, you get the run id back instead of the rows: ``` RUNNING web.search df7b5519-ccd3-448d-b5dc-01a4273e0a6a held $0.0210 price up to $1.00 for a single call, and never more than that. Billed on actual usage, minimum $0.0020. The hold scales with what you request. You are charged for what the call actually did, and nothing if it cost nothing. Poll it: goro runs get -r df7b5519-ccd3-448d-b5dc-01a4273e0a6a Or wait: goro runs get -r df7b5519-ccd3-448d-b5dc-01a4273e0a6a --wait ``` Add `--wait` to block until it finishes instead. The CLI polls with exponential backoff up to `--timeout` seconds: ```bash theme={null} goro run -s web.search -i '{"queries":"aeropress vs french press"}' --wait ``` Ctrl-C during `--wait` stops the waiting, not the run. The run keeps going and you can pick it up later with `goro runs get -r `. If the timeout passes and the run is still going, the CLI exits `4` and tells you how to keep waiting. The run itself is unaffected. ## Saving the output Human output prints the first three rows and then points you at the full result. To keep everything: ```bash theme={null} # machine-readable on stdout goro run -s web.search -i '{"queries":"ai agents"}' -j # rendered on stdout, full JSON in a file goro run -s web.search -i '{"queries":"ai agents"}' -o results.json ``` `runs get` returns a run's status and cost forever, but its rows only until you collect them and at most 24 hours after the run finished. After that `output` is `null`. Capture anything you need with `-o` or `-j`. ## When it does not work | Situation | Output | Exit | | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ---- | | Input is not valid JSON | `Input is not valid JSON: ...` | `1` | | Input fails the tool's schema | `/queries: must have required property 'queries'` | `1` | | `--image` file is missing, empty, too large, or not a JPEG, PNG or WEBP | `--image file "./photo.jpg" is not a JPEG, PNG or WEBP image.` | `1` | | Key missing or revoked | `Unknown or revoked API key` | `2` | | No active plan | `subscription_required` with the billing URL | `3` | | Balance too low | `Insufficient funds: this run requires a hold of $0.0210.` plus the top-up URL | `3` | | Budget cap would be breached | `budget_exceeded` with the period and cap | `3` | | Provider failed | `provider_error` with the run id | `4` | A 402 prints what it needs and where to fix it: ``` Error Insufficient funds: this run requires a hold of $0.0210. Top up to continue. Needed: $0.0210 Top up: https://app.usegoro.ai/billing ``` Poll a run, and see what you have spent. # goro runs Source: https://docs.usegoro.ai/cli/runs Poll a single run to completion, or list what you have run recently and what it cost. ## runs get ```bash theme={null} goro runs get -r d5f6185e-7faa-4fd3-b81b-0d02f4f9e918 ``` ``` COMPLETED web.search d5f6185e-7faa-4fd3-b81b-0d02f4f9e918 cost charged $0.0105 (1 result, $0.0105 each) held $0.0210, the rest was released finished 2026-07-31T09:25:44.927+00:00 ``` | Option | Meaning | | --------------------- | ------------------------------------------- | | `-r`, `--run` | The run id. Required. | | `--wait` | Poll until the run reaches a terminal state | | `--timeout ` | How long `--wait` waits. Default 300. | With `--wait`, the CLI polls with exponential backoff (1s, then 1.5s, 2.25s and so on, capped at 8s) until the run is `COMPLETED`, `FAILED`, `STOPPED` or `TIMED_OUT`. ```bash theme={null} goro runs get -r df7b5519-ccd3-448d-b5dc-01a4273e0a6a --wait --timeout 600 ``` The exit code follows the run, not the HTTP call: `COMPLETED` exits `0`, anything else exits `4`. Polling is free and idempotent. It is also what advances an in-flight run, so polling a slow run is the normal thing to do, not a workaround. ### JSON shape ```bash theme={null} goro runs get -r d5f6185e-7faa-4fd3-b81b-0d02f4f9e918 -j ``` ```json theme={null} { "run_id": "d5f6185e-7faa-4fd3-b81b-0d02f4f9e918", "endpoint": "web.search", "status": "COMPLETED", "output": null, "output_count": 1, "quoted_cost_micro": 21000, "quoted_cost_usd": "$0.0210", "cost_micro": 10500, "cost_usd": "$0.0105", "error": null, "created_at": "2026-07-31T09:25:04.257271+00:00", "started_at": "2026-07-31T09:25:06.309+00:00", "finished_at": "2026-07-31T09:25:44.927+00:00" } ``` `quoted_cost_*` is the hold that was taken up front. `cost_*` is what you were actually charged. Every amount comes as both an integer in micro-USD (one millionth of a dollar, so there is no floating point drift) and a formatted string. `output` is `null` here once the rows have been collected, or once the run is more than 24 hours old. Capture them with `goro run ... -o file.json`. ## runs list ```bash theme={null} goro runs list ``` ``` 3 runs RUN ID ENDPOINT STATUS ROWS COST CREATED d7bdf7f4-00a2-4aa1-a637-7519c3c353e9 web.search COMPLETED 1 $0.0105 2026-07-31 09:27:48 df7b5519-ccd3-448d-b5dc-01a4273e0a6a web.search COMPLETED 1 $0.0105 2026-07-31 09:27:09 d5f6185e-7faa-4fd3-b81b-0d02f4f9e918 web.search COMPLETED 1 $0.0105 2026-07-31 09:25:04 ``` | Option | Meaning | | ------------------- | --------------------------------------------- | | `-n`, `--limit` | How many runs to return. Default 20. | | `--cursor ` | Continue from a previous page's `next_cursor` | Newest first. When there is another page, the CLI prints the exact command to fetch it. ## balance ```bash theme={null} goro balance ``` ``` Wallet available $1.9685 cash $1.9685 ``` `available` is what you can spend right now: cash plus any unexpired included or promotional credit, minus anything currently held by a running call. That credit is listed separately when you have some, because it is spent before cash. An empty wallet says so, and points at the top-up page: ``` Wallet available $0.00 cash $0.00 Empty. Top up at https://app.usegoro.ai/billing ``` Piping to jq, retry loops, and batch runs. # For humans Source: https://docs.usegoro.ai/guide/quickstart-cli Sign in, get a key, make a call from your terminal, and see exactly what it cost. You do not need an agent to use Goro. A key and `curl` are enough to try the catalog, and the dashboard shows what every call cost. ## 1. Sign in Go to [app.usegoro.ai](https://app.usegoro.ai) and sign in with Google or with an emailed code. A workspace and a wallet are created for you. ## 2. Pick a plan Running anything, even your first call, needs an active plan. Open **Billing** and subscribe to Build ($19 a month, $25 of included credit) or Scale ($99 a month, $150 of included credit). Signing up and browsing the catalog are free; running a tool is not. ## 3. Create a key Open **API keys** and create one. You see the full key exactly once, at creation, so copy it then. Goro stores only a hash of it, which means we cannot show it to you again and a database leak cannot leak a usable key. ```bash theme={null} export GORO_API_KEY="goro_live_..." ``` Check it works: ```bash theme={null} curl https://api.usegoro.ai/v1/whoami \ -H "Authorization: Bearer $GORO_API_KEY" ``` ## 4. Find a tool You do not need to know the catalog. Describe what you want. ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/discover \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "coffee shops in bucharest"}' ``` Each result carries a slug, guidance on when to use it, and a price. You can also browse the same catalog under **Tools** in the dashboard, or in the [tool reference](/tools/overview) here. ## 5. See what it takes ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/inspect \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug": "maps.places"}' ``` `inspect` returns the tool's real input schema and a `sample_input` you can send almost as-is. ## 6. Run it ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "slug": "maps.places", "input": { "searchStringsArray": ["specialty coffee"], "locationQuery": "Bucharest, Romania", "maxCrawledPlacesPerSearch": 5 } }' ``` A fast call returns `200` with the rows in `output` and `cost_usd` telling you what it cost. A slower one returns `202` with a `run_id`, and you poll: ```bash theme={null} curl https://api.usegoro.ai/v1/runs/$RUN_ID \ -H "Authorization: Bearer $GORO_API_KEY" ``` Goro does not store results. The rows arrive in the response that finishes the run and are never written to our database, so pipe the output to a file if you want to keep it. ## 7. Watch the money ```bash theme={null} curl https://api.usegoro.ai/v1/wallet/balance \ -H "Authorization: Bearer $GORO_API_KEY" ``` The dashboard covers the same ground with more context: | Page | What it is for | | ------------ | ----------------------------------------------------------------------------------------------- | | **Wallet** | Your plan, one-time top-ups (minimum \$5), the full ledger, and the Stripe portal for your card | | **Usage** | What you have spent over time, run by run | | **Budgets** | A daily or monthly cap, set to block calls or just notify you | | **API keys** | Create and revoke keys. Revoking takes effect immediately | A plan renews on its date, a top-up happens when you make one, and nothing else charges your card. Nothing is ever charged while you are not on the page. Every endpoint, field and error code. Rates, totals and ceilings. # For AI agents: MCP Source: https://docs.usegoro.ai/guide/quickstart-mcp Add Goro to Claude, Cursor or any MCP client as a connector, and your agent gets the whole catalog as eight tools. Goro runs a remote MCP server. Add one URL to your client and the agent gets eight tools that reach the entire catalog, on the same balance and the same prices as the HTTP API. ``` https://mcp.usegoro.ai/mcp ``` ## 1. Add the connector In Claude, Cursor, or any MCP client that supports remote servers, add a custom connector with the URL above. The client handles the rest: it registers itself, opens Goro's consent screen in a browser, and stores the token it gets back. Transport is Streamable HTTP. The server is stateless per request, so there is no session to keep alive and nothing to reconnect. ## 2. Authorise it The consent screen asks for a Goro API key, the `goro_live_` one from [app.usegoro.ai](https://app.usegoro.ai). That key identifies which workspace and balance the connector will spend from. Paste it once, approve, and the client is connected. Two scopes exist, and a client that does not ask for a narrower grant gets both: | Scope | Grants | | ------------ | ---------------------------------------------------------------------------------------------------------------------------- | | `tools:read` | `discover_tools`, `inspect_tool`, `create_upload`, `get_run`, `wallet_balance`, `remember_tool_choice`, `forget_tool_choice` | | `tools:run` | `run_tool` | Access tokens last an hour and the client refreshes them silently. Refresh tokens last 30 days and rotate on every use. Building the client yourself rather than using an existing one? The OAuth flow is documented end to end in the [OAuth quickstart](/guide/quickstart-oauth). Connecting does not mean the workspace can run tools yet. `discover_tools`, `inspect_tool` and `wallet_balance` work right away. `run_tool` and `create_upload` need an active plan, Build or Scale, and answer `subscription_required` until the workspace has one. Pick a plan at [app.usegoro.ai/billing](https://app.usegoro.ai/billing). ## 3. The eight tools | Tool | Scope | When the agent calls it | | ---------------------- | ------------ | ------------------------------------------------------------------------------------------------------ | | `discover_tools` | `tools:read` | First, whenever the task needs live or external data and the exact slug is unknown | | `inspect_tool` | `tools:read` | Before running a tool whose input shape is not already known | | `create_upload` | `tools:read` | Before `run_tool`, whenever the tool needs a file. Needs an active plan, even though it spends nothing | | `run_tool` | `tools:run` | To actually execute a tool. Needs an active plan. This spends money | | `get_run` | `tools:read` | To poll a `run_tool` call that came back with a `run_id` | | `wallet_balance` | `tools:read` | To check available funds, or after an `insufficient_funds` error | | `remember_tool_choice` | `tools:read` | Only when you explicitly say a choice should stick. It spends nothing | | `forget_tool_choice` | `tools:read` | When you want to be offered the choice again | That is the complete list. There is no per-tool MCP surface: the 80 catalog tools are reached through `run_tool` by slug, which is what keeps the tool list small enough for an agent to reason about. ### What the agent sees `discover_tools` takes a plain-language `query` and an optional `limit` (default 5, maximum 20). Each result carries a slug, guidance on when to use it, its price and a fillable sample input, so the agent can usually skip `inspect_tool`. ## Who picks the tool You do. Goro never chooses one: `run_tool` takes an exact slug, so when several tools can do a job, the choice happens in the agent. By default it is not allowed to make that choice quietly. `discover_tools` returns a `choose` field telling the agent to show you the candidates with their prices and ask which you want. That matters because the prices are not close: five tools can make you an image, from $0.01 to $0.27 a call, and being charged for one you were never shown is not a good surprise. Three things skip the question: * **You named the tool.** "Use nano banana pro for this" is already an answer. * **Only one tool matches.** Nothing to choose between, though the agent should still tell you what it is about to spend. * **You said to always use one.** Tell the agent "always use X for images", "remember that", or "stop asking me", and it calls `remember_tool_choice`. From then on `discover_tools` returns `your_preference` for that category and the agent uses it without asking. A preference is saved **only when you ask for one**. Picking a tool for a single job is not a standing instruction, and treating it as one would mean you are never offered the cheaper option again. One saved choice per category (image, video, voice, search, social, maps, people, ecommerce). Say you want to pick again and the agent calls `forget_tool_choice`. This is an instruction to the agent, not a lock. Goro can tell a client to ask you and can refuse to guess on your behalf, but it cannot reach into the client and force a prompt. A well-behaved agent follows it; treat `remember_tool_choice` as the reliable control, since that one is enforced server-side. The HTTP API and the CLI are unaffected: both already require you to name a slug, so there is nothing for anyone to pick. `run_tool` blocks for up to about 25 seconds. A fast tool returns its rows in that same response. A slower one returns a `run_id` and an explicit instruction to poll `get_run` until the status is terminal. `wallet_balance` returns the available balance, the workspace's `plan`, the unexpired included credit, `renews_at` and a `top_up_url`. The URL and, on a plan, the renewal date are what an agent should surface to a human when funds run out. ### Sending a file One rule, and it matters more than it looks: an agent must never read a file into the conversation in order to send it. Tool arguments pass through the context window exactly like tool results do, so a 3 MB photograph sent as `image_base64` is roughly a million tokens, gets truncated, and fails the run with something that reads like file corruption. `create_upload` is the way round it. It takes a `content_type`, returns a short handle plus a URL to PUT the file to, and includes the exact `curl` to run. The agent uploads the file with its own shell, then passes the handle as `image_handle` in `run_tool`. The bytes never enter the conversation, the ceiling goes from 3 MB to 10 MB, and the handle itself is about thirty characters. Do not shrink the image to make it fit: size was never the problem the upload path is solving. [Uploads](/api/uploads) documents the whole flow. ## 4. Errors an agent will meet Tool errors carry the same `code` and `message` as the HTTP API, so one set of instructions covers both surfaces. | `code` | What the agent should do | | ----------------------- | ---------------------------------------------------------------------------------------------------------- | | `validation_error` | Read the `errors` array, fix every field, retry once | | `subscription_required` | Stop. The workspace has no active plan. Tell the human to pick one at the `billing_url` in the body | | `insufficient_funds` | Stop. Tell the human the amount, the `top_up_url` and, if `renews_at` is set, when included credit returns | | `budget_exceeded` | Stop. The wallet is fine, a spend cap is blocking. Do not ask for a top-up | | `not_found` | The slug does not exist. Run `discover_tools` again | | `forbidden` | The token is missing the scope the tool needs. Reauthorise | | `provider_error` | The tool failed and nothing was charged. Safe to retry | ## What this shares with the API Everything. The MCP tools are thin wrappers over the same code paths as `/v1/discover`, `/v1/inspect`, `/v1/uploads`, `/v1/run`, `/v1/runs/{id}` and `/v1/wallet/balance`. Same catalog, same holds, same charges, same ledger. Rows arrive in the `run_tool` response, or in the `get_run` poll that finds the run finished. Polling a finished run is the supported way to collect a slow result. Results are deleted once collected, and after 24 hours regardless, so have the agent write down anything it will need later. When the result is a media link, from a voice or image tool, have it download the file too: those links expire on the same clock or on the model provider's. The endpoints behind the eight tools. # For applications: OAuth Source: https://docs.usegoro.ai/guide/quickstart-oauth Connect your product to Goro on a user's behalf with OAuth 2.1, PKCE and dynamic client registration. Use this when you are building the client rather than configuring one: your own MCP client, an agent platform that connects on behalf of its users, or any application that should spend a user's Goro balance rather than your own. The authorisation server is at `https://mcp.usegoro.ai`, and the resource it issues tokens for is the MCP endpoint at `https://mcp.usegoro.ai/mcp`. OAuth access tokens authenticate the MCP endpoint only. The `/v1` HTTP API takes `goro_live_` API keys and nothing else. If you want the HTTP API, you want the [Skill quickstart](/guide/quickstart-skill). ## 1. Read the metadata Both documents are public and need no credentials. ```bash theme={null} curl https://mcp.usegoro.ai/.well-known/oauth-authorization-server ``` ```json theme={null} { "issuer": "https://mcp.usegoro.ai", "authorization_endpoint": "https://mcp.usegoro.ai/oauth/authorize", "token_endpoint": "https://mcp.usegoro.ai/oauth/token", "registration_endpoint": "https://mcp.usegoro.ai/oauth/register", "response_types_supported": ["code"], "grant_types_supported": ["authorization_code", "refresh_token"], "code_challenge_methods_supported": ["S256"], "token_endpoint_auth_methods_supported": ["none"], "scopes_supported": ["tools:read", "tools:run"] } ``` `https://mcp.usegoro.ai/.well-known/oauth-protected-resource` describes the resource itself: which authorisation server protects it, the same two scopes, and that bearer tokens go in the header. An unauthenticated request to `/mcp` answers `401` with a `WWW-Authenticate` header pointing at that document, which is how a client discovers all of this without being told. ## 2. Register a client Dynamic client registration is open, per RFC 7591. There is no client secret: Goro issues public clients and PKCE is the proof of possession. ```bash theme={null} curl -X POST https://mcp.usegoro.ai/oauth/register \ -H "Content-Type: application/json" \ -d '{ "client_name": "Northwind Agents", "redirect_uris": ["https://app.northwind.example/callback"] }' ``` ```json theme={null} { "client_id": "goro_client_9SjK2mQ1xR7bT4vZ", "client_name": "Northwind Agents", "redirect_uris": ["https://app.northwind.example/callback"], "token_endpoint_auth_method": "none", "grant_types": ["authorization_code", "refresh_token"], "response_types": ["code"] } ``` `redirect_uris` must be a non-empty array of http or https URIs. Anything else answers `400 invalid_client_metadata`. Register once and store the `client_id`. This endpoint is rate limited per IP, 10 per minute and 50 per day, so do not register on every connection. ## 3. Send the user to authorise Generate a PKCE verifier and its S256 challenge, then redirect the user to the authorisation endpoint. ``` https://mcp.usegoro.ai/oauth/authorize ?client_id=goro_client_9SjK2mQ1xR7bT4vZ &redirect_uri=https://app.northwind.example/callback &response_type=code &code_challenge= &code_challenge_method=S256 &scope=tools:read%20tools:run &state= ``` | Parameter | Required | Notes | | ----------------------- | -------- | ----------------------------------------------- | | `client_id` | yes | From registration | | `redirect_uri` | yes | Must exactly match one you registered | | `code_challenge` | yes | PKCE is mandatory | | `code_challenge_method` | | `S256` only. Defaults to `S256` when omitted | | `response_type` | | `code`. Any other value is rejected | | `scope` | | Space-delimited. Omitting it grants both scopes | | `state` | | Returned to you unchanged | | `resource` | | If sent, must be `https://mcp.usegoro.ai/mcp` | The user lands on Goro's consent screen, where they approve the connection with their `goro_live_` API key. That key is what binds the grant to their workspace and balance. On approval Goro redirects to your `redirect_uri` with `code` and your `state`. Errors that happen after the redirect URI has been validated come back the same way, as `error` and `error_description` on the query string. Errors before that point, an unknown `client_id` or a redirect URI that does not match, answer directly as JSON and never redirect. Authorisation codes are single use and live for 10 minutes. ## 4. Exchange the code ```bash theme={null} curl -X POST https://mcp.usegoro.ai/oauth/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=authorization_code" \ -d "code=goro_ac_..." \ -d "redirect_uri=https://app.northwind.example/callback" \ -d "client_id=goro_client_9SjK2mQ1xR7bT4vZ" \ -d "code_verifier=" ``` ```json theme={null} { "access_token": "goro_at_...", "refresh_token": "goro_rt_...", "token_type": "Bearer", "expires_in": 3600, "scope": "tools:read tools:run" } ``` All four fields are required on this grant. A missing one answers `400 invalid_request`. ## 5. Refresh ```bash theme={null} curl -X POST https://mcp.usegoro.ai/oauth/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=refresh_token" \ -d "refresh_token=goro_rt_..." \ -d "client_id=goro_client_9SjK2mQ1xR7bT4vZ" ``` Both fields are required. You get a new pair back, and the refresh token you sent is retired. | Lifetime | Value | | ------------------ | ----------------------------- | | Authorisation code | 10 minutes, single use | | Access token | 1 hour | | Refresh token | 30 days, rotated on every use | Replaying a burned authorisation code, or a refresh token that has already been rotated, revokes the entire family of tokens descended from that authorisation. This is deliberate: a replay means the token leaked, and the safe response is to end the session. Store the newest refresh token and never retry an old one. ## 6. Call the MCP endpoint ```bash theme={null} curl -X POST https://mcp.usegoro.ai/mcp \ -H "Authorization: Bearer goro_at_..." \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' ``` You get the eight tools described in the [MCP quickstart](/guide/quickstart-mcp). `POST` carries every JSON-RPC message, and `DELETE` is accepted for clients that send it. `GET` answers `405`: this server offers no server-sent event stream, and conforming clients treat that as "no stream" and carry on. An expired or revoked token answers `401` with a `WWW-Authenticate` header, at which point refresh and retry. Scope is enforced per tool. Calling `run_tool` with a token that only carries `tools:read` answers a `forbidden` tool error naming the missing scope. ## Error shapes The OAuth endpoints answer `{"error": ..., "error_description": ...}`, per RFC 6749, which is a different shape from the `{"code", "message"}` used everywhere else in Goro. | `error` | When | | --------------------------- | -------------------------------------------------------------------- | | `invalid_request` | A required parameter is missing, or PKCE is absent or not S256 | | `invalid_client` | Unknown `client_id` | | `invalid_client_metadata` | Registration payload is not acceptable | | `invalid_grant` | Unknown, expired, replayed or already-consumed code or refresh token | | `invalid_scope` | A scope outside `tools:read` and `tools:run` | | `invalid_target` | A `resource` other than `https://mcp.usegoro.ai/mcp` | | `unsupported_grant_type` | A `grant_type` other than the two supported | | `unsupported_response_type` | A `response_type` other than `code` | | `rate_limited` | Too many registration or authorisation attempts from one IP | The eight tools your token unlocks. # For AI agents: Skill Source: https://docs.usegoro.ai/guide/quickstart-skill Teach an agent the whole gateway with one markdown file and an API key. No client library, no MCP client. If your agent can make HTTP requests and read instructions, it does not need a Goro SDK. It needs a key and a page of rules. This is the right path for a coding agent, a background worker, or anything running in a loop you control. If you are wiring up Claude, Cursor or another MCP client, use the [MCP quickstart](/guide/quickstart-mcp) instead. ## 1. Get a key Create a workspace at [app.usegoro.ai](https://app.usegoro.ai), pick a plan under **Billing** (Build or Scale), and generate an API key. Running a tool needs an active plan: without one every call to `run` answers `402 subscription_required`, so pick a plan before you wire up the agent. Put it where your agent keeps secrets, conventionally as `GORO_API_KEY`. ## 2. Give the agent the rules The full instruction file is published at [usegoro.ai/SKILL.md](https://usegoro.ai/SKILL.md). Point your agent at that URL, or save a copy as a skill file, a system prompt, or whatever your framework calls its instruction file. Prefer the hosted copy where you can. It is generated against the live API and covers the parts an agent gets wrong on its own: that result counts apply per target, that a scrape's quoted price is a ceiling while an image or voice price is exact, that a media link expires, and that it should reach for your own tools before spending the balance. The short version below is the same contract, condensed. ```markdown theme={null} # Goro Goro is a tool gateway. One key, one balance, pay per call. You never sign up for individual providers or hold their API keys. Base URL: https://api.usegoro.ai Auth: send `Authorization: Bearer $GORO_API_KEY` on every request. ## The loop 1. DISCOVER. You do not need to know the catalog. Describe the job. POST /v1/discover {"query": "recent posts from a tiktok creator", "limit": 5} Returns {"items": [...]}, best first. Each item has `slug`, `when_to_use`, a `price` object, and a `sample_input` you can fill in. An empty `items` means nothing in the catalog answers that query: say so, do not invent a slug. 2. INSPECT, when you are unsure of the input shape. POST /v1/inspect {"slug": "tiktok.profile"} Returns `input_schema` (real JSON Schema, validated on every run), `sample_input`, `output_sample` and `price`. Skip this when discover already showed you the shape. 3. RUN. This spends real money. Only call it when you intend to execute. POST /v1/run {"slug": "tiktok.profile", "input": {...}} 200 means finished: the rows are in `output`, and `cost_usd` is what it cost. 202 means still running: poll `status_url` until `status` is COMPLETED, FAILED, STOPPED or TIMED_OUT. 502 means the provider failed and you were not charged. 4. POLL, only after a 202. GET /v1/runs/{run_id} Safe to repeat. A finished run holds its rows until you take them, so a poll that arrives after it finished still carries them. They are deleted once collected and 24 hours after the run either way, so keep anything you need long term. If the result is a media link, download the file: those expire too. ## Money rules - Check the balance with GET /v1/wallet/balance before a large job. - Prices are visible before you spend, but they do not all mean the same thing. Read `price.type`. Where `billed_on_actual_usage` is true the number is a MAXIMUM for that request and the real charge is usually lower. Where it is absent, on the flat-priced image tools, the number IS the price. - Amounts come as integer micro-USD (1 USD = 1,000,000) plus a human `_usd` string. Do maths on the integers. - Failed, stopped and timed-out calls are never charged. Retrying is safe. - On 402 `subscription_required`, the workspace has no active plan. Stop and tell your human to pick one at the `billing_url` in the body. Do not retry. - On 402 `insufficient_funds`, stop and tell your human the amount and the `top_up_url` in the body. If the body's `renews_at` is set, say that the plan's included credit comes back on that date. Do not keep retrying. - On 402 `budget_exceeded`, the wallet is fine and a spend cap is the blocker. Say that instead of asking for a top-up. ## Errors Every error body is {"code": ..., "message": ...}. Branch on `code`. 400 validation_error carries an `errors` array with a JSON pointer per bad field: correct all of them and retry once. 404 means the slug does not exist. 429 means slow down. 502 and 503 mean retry later. ``` ## 3. Watch it work The first call an agent makes should be `discover`. If it goes straight to `run` with a slug it guessed, the instructions did not land. ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/discover \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "tweets about ai agents"}' ``` Every run shows up in the dashboard's usage view with its cost, and in `GET /v1/wallet/activities` as ledger entries. Every endpoint, field and error code. # Introduction Source: https://docs.usegoro.ai/index One connection gives your agent search, scraping and enrichment across the real world. Goro is a pay-per-call gateway. Your agent connects once, and gets a catalog of tools that reach the real world: search, social profiles and posts, maps and reviews, product and jobs data, image and video generation, and voice in both directions. You hold one balance and each call draws from it. A plan fills it every month with more credit than the plan costs, and once you are on one you can add credit as you go too. A scrape bills the published rate for what it actually consumed; an image or a voice call bills a price you can read before you make it. ## Why it exists Every one of these tools already exists somewhere, behind its own account, its own key and its own bill. Wiring five of them into an agent means five signups, five credentials and five invoices before it makes a single request. Goro is one credential and one balance for all of them. ## How a call works Ask what can answer your question. `discover` ranks the catalog against a plain-English query and returns each option with its price. Read the exact input a tool expects, plus a sample you can fill in. Make the call. Goro holds an estimate against your balance, runs the tool, then charges what it actually cost and releases the rest. Discover and inspect are free and need no plan. Run needs an active plan: without one it answers `402 subscription_required` instead of running. See [Pricing](/pricing#plan-rules). ## Two surfaces, one catalog Goro speaks HTTP and MCP, over the same catalog, the same balance and the same prices. | Surface | Endpoint | Authenticates with | | ---------- | ---------------------------- | ---------------------------------------- | | HTTP API | `https://api.usegoro.ai` | A `goro_live_` API key as a Bearer token | | MCP server | `https://mcp.usegoro.ai/mcp` | An OAuth 2.1 access token | The MCP server exposes eight tools (`discover_tools`, `inspect_tool`, `create_upload`, `run_tool`, `get_run`, `wallet_balance`, `remember_tool_choice`, `forget_tool_choice`), each a thin wrapper over the same code the HTTP endpoints call. ## What Goro keeps, and for how long Goro records what a call cost, how many rows it returned and when it ran. That part is permanent. The rows themselves are held only until you collect them. A finished run keeps its results so a slow job stays collectable, then they are deleted shortly after your first fetch, and in all cases 24 hours after the run finished. The window is deliberately the shortest one that makes a paid run reliably deliverable: this catalog can reach personal data about people who have no relationship with us. Capture what you need when you get it. It has a consequence worth planning for. A run's rows can stop being available even though its status and cost stay readable forever, so keep the rows from the response that hands them to you. The same goes double for media: a voice call answers with a link to audio that expires on that same 24 hour clock, and an image call answers with links held by the model provider. Download the file, do not save the URL. ## Start here Teach an agent the API in one file, no client library. Add Goro to Claude, Cursor or any MCP client as a connector. Let your product connect on a user's behalf, with their balance. Get a key, make a call, watch the balance move. # Pricing Source: https://docs.usegoro.ai/pricing Plans that include more credit than they cost, one-time top-ups, and what a call costs: an exact price where one exists, a rate and a ceiling where it cannot exist, and never a charge above the ceiling. ## Plans One balance, and every call draws from it. Running a tool needs an active plan. A plan fills the balance every month with more credit than the plan costs, and once you are on one, a one-time top-up adds more credit whenever you like. Credit you bought stays yours until you spend it. Signing up is free, and so is looking around: browsing the catalog, `discover`, `inspect`, and reading your runs and balance never need a plan. | | Build | Scale | | ----------------------------- | ------------------------------------------------ | -------------------------- | | Price | \$19 a month | \$99 a month | | Included credit | \$25 every billing period | \$150 every billing period | | Price per call | The tool's published price | The same | | One-time top-ups | $5, $20, $100, or any amount from $5 to \$10,000 | The same | | Tools, budgets, MCP, API, CLI | Everything | Everything | Three rules carry most of the weight. Included credit lands when Stripe confirms the invoice paid and expires at the end of that billing period, so it does not roll over. Purchased credit never expires, and it is available only to a workspace with an active plan, as overage once included credit runs out. Nothing is ever charged to a card while you are not on the page. The rest is under [Plan rules](#plan-rules) below. ## Three ways a tool prices Goro fronts three providers and they meter their work in different ways, so a quoted price does not mean the same thing everywhere. Which shape applies is written on the tool's page, and comes back as `price.type` from `discover` and `inspect`. | Shape | What you are quoted | Where it applies | | ---------- | ---------------------------------------------------------------------------------- | ------------------------------------------- | | Per result | A rate per row plus a ceiling. The total follows the rows the call really produced | Search, social, maps, commerce, people data | | Flat | One exact price per call, the same every time | Image generation, and `video.veo` | | Metered | An exact rate against something measurable in your request, plus a ceiling | Voice, and every other video endpoint | Only the first has a total that cannot be known before the call, and most of this page is about that case. The other two are arithmetic you can do in advance. ### Flat An image generation call costs what it costs, whatever comes back: $0.0120 on Z-Image, $0.06 on Grok Imagine, $0.0825 on Seedream 5 Lite, $0.18 on Nano Banana 2. There is no ceiling to read and nothing to settle afterwards, and a call that fails is not charged at all. `video.veo` prices the same way, at \$3.8250 a call, because it only makes videos of one length. ### Metered A voice call bills a published rate against a quantity you can measure yourself. Text to speech is $0.0450 per 1,000 UTF-8 bytes of the text you send, and no call can exceed $0.36. Speech to text is $0.0003 per second of audio, which works out at $1.08 an hour, and no call can exceed \$1.08. The other five video endpoints bill the same way, against the seconds of video you ask for. The rate is per second and differs by model, from $0.0450 on Grok Imagine to $0.6150 on Seedance 5 Pro, and `duration` is the only field that changes the price. The ceiling on one of those is that rate times the longest clip the endpoint accepts. The rate is exact, so the total is your input size times the rate. The ceiling is still a ceiling: it is the price of the largest request the endpoint takes, not a typical call. ## Three numbers, three jobs This is the per-result case, the one every search, social, maps, commerce and people tool uses. Every one of those tools publishes a rate per result, for example $0.0069 per profile or $0.0075 per result page. That is what one row costs you, and it is on the tool's page in the [catalog](/tools/overview). The total is the rate times the results the call actually produced. Ask for 3 rows and you pay for 3. Ask for 300 and the call finds 40, you pay for 40. Before the call runs, `discover` and `inspect` return a ceiling for your specific request. You are never charged above it. It is a maximum, not an estimate of what you will pay. So a call reads like this: $0.0069 per profile, this request is capped at $0.14, the final charge lands wherever the results land. ## Why the total is not fixed up front How many rows a request produces is not knowable before it runs. A search for a niche term returns four results; the same query next week returns forty. Goro bills what the call consumed rather than an average that would overcharge the small calls to cover the large ones. Before the call runs, Goro reserves an amount against your balance, sized from what you asked for. A small request holds a small amount, a large one holds more. The hold never exceeds the ceiling. When the call finishes, Goro works out what it actually did, takes that amount, and releases the rest of the hold. ## A call that returns nothing If a call completes with no results, the per-call floor is waived and you pay only the published rate for what it actually did, which for an empty result is fractions of a cent. If the call cost nothing to serve, it bills nothing at all and comes back marked free. You are never charged for rows you did not receive. A flat-priced or metered call is different in kind: it either produced the thing you asked for, in which case it costs its published price, or it failed, in which case it costs nothing. ## Holds are not charges A hold reserves balance, it does not spend it. If a call fails, times out, or you stop it, the whole hold is released and nothing is charged. Your available balance is what you can spend right now: the credit you bought, plus any unexpired included or promotional credit, minus anything currently held by a running call. ## Budgets You can set a daily or monthly spend cap on a workspace, and choose whether hitting it blocks further calls or just notifies you. The cap is enforced when the hold is taken, so a call that would breach it never runs. A blocked call answers `402` with the code `budget_exceeded`. ## Plan rules Running a tool needs an active plan, Build or Scale, with a Stripe status of trialing, active, or past\_due (Stripe is still retrying the card, and credit already granted keeps the expiry it was given). Signing up, browsing the catalog, `discover`, `inspect`, and reading your runs and balance never need one. Without an active plan, running a tool (`POST /v1/run`, MCP `run_tool`, the CLI `run`) and creating an upload (`POST /v1/uploads`, MCP `create_upload`) answer `402` with the code `subscription_required`, and nothing is charged or held: ```json theme={null} { "code": "subscription_required", "message": "Goro needs an active plan to run tools. Pick one at https://app.usegoro.ai/billing", "billing_url": "https://app.usegoro.ai/billing", "top_up_url": "https://app.usegoro.ai/billing" } ``` A workspace that has never subscribed keeps its wallet, including any purchased or promotional credit sitting on it. It just cannot spend that credit on a run until it picks a plan. Included credit lands in your balance when Stripe confirms the invoice paid, and expires at the end of that billing period. What you have not spent by then is gone. It does not roll over. Purchased credit never expires, and it is sold only to a workspace with an active plan: a checkout for one-time credit also answers `subscription_required` on a workspace with no plan. Included credit is spent before purchased credit, so a top-up you made sits untouched until the period's included credit is used up. When both are gone, a run fails with `insufficient_funds` instead. The error carries your `plan`, the `top_up_url` and, on a plan, `renews_at`, and the message names the renewal date, so an agent can tell you whether to add credit now or wait. Nothing is ever charged to your card while you are not on the page: a plan fee is charged on its renewal date, a top-up when you make one, and nothing else. You change or cancel a plan from the Wallet page, through the Stripe customer portal, and both take effect at the next renewal. Cancelling stops the next charge; the cycle you already paid for runs to its end with its included credit, and you can keep running tools until it does. If a renewal payment fails, Stripe retries the card on its own schedule and emails you about it: included credit ends at the period end it already had, but the plan itself stays active while retries continue, so purchased credit keeps working. If Stripe gives up, the plan ends: you cannot run tools again until you subscribe. Purchased credit is never lost. It stays on the wallet and starts working again the moment you resubscribe. Plan fees are billed monthly in advance and are not refundable. There is no free trial on a plan. ## Reading prices from the API `discover` and `inspect` return a `price` object per tool. Read `type` first, because it decides which of the other fields are there: `usage_based` and `metered` carry a ceiling in `max_usd`, `per_call` carries a fixed `base_usd` and no ceiling at all, and `per_call_variant` carries a `variants` list, one exact price per value of the input named in `selector`. `billed_on_actual_usage` is the field to branch on. Where it is `true`, the total depends on the call and `max_usd` is a maximum rather than a price. Where it is absent, the quoted number is the price. Either way an agent can tell them apart without parsing English. Field by field, see the [API reference](/api/discover). Questions about a specific charge? Email [hello@usegoro.ai](mailto:hello@usegoro.ai) with the run id and we will break it down. # Autovit listings Source: https://docs.usegoro.ai/tools/automotive/autovit-listings Used-car listings from Autovit.ro, Romania's main car marketplace, filtered by make, model, year, price, mileage and seller type. Each listing returns price and Used-car listings from Autovit.ro, Romania's main car marketplace, filtered by make, model, year, price, mileage and seller type. Each listing returns price and currency, year, mileage, fuel, gearbox, city and county, the ad URL, the photo count with photo URLs, and the seller: dealer or private, and the seller name and id. Use for Romanian car-market work: finding dealers who sell a make in a price band, sizing a seller by how many ads they run, or spotting ads with few photos for a photography or listing-improvement offer. sellerType dealer narrows to professional sellers. There is no start fee here, so small calls cost no more per listing than large ones; maxItems is exactly what you pay for, one billed row per listing. Sort by created\_at:desc to see freshly posted ads first. Use mobilede.listings and autoscout24.listings for Germany and the rest of the EU. ## Price **\$0.0030 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ | | `make` | `string` | | Make as the lowercase autovit.ro slug, e.g. dacia, volkswagen, bmw. | | `model` | `string` | | Model as the lowercase autovit.ro slug, e.g. logan, golf, seria-3. Requires make. | | `sortBy` | `filter_float_price:asc`, `filter_float_price:desc`, `filter_float_mileage:asc`, `filter_float_mileage:desc`, `filter_float_engine_power:asc`, `filter_float_engine_power:desc`, `created_at:desc`, `relevance_web` | | Result order. created\_at:desc lists the newest ads first. Defaults to `"filter_float_price:asc"`. | | `yearTo` | `integer` | | Production year up to. | | `priceTo` | `integer` | | Maximum price in the ad's own currency (EUR or RON, set per ad). | | `maxItems` | `integer` | | How many listings to return, and the number you pay for: one billed row per listing. Capped at 500 per call. Defaults to `50`. | | `yearFrom` | `integer` | | Production year from. | | `mileageTo` | `integer` | | Maximum mileage in km. | | `priceFrom` | `integer` | | Minimum price in the ad's own currency (EUR or RON, set per ad). | | `sellerType` | `private`, `dealer` | | Only private sellers or only dealers. Leave out for both. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"autovit.listings","input":{"maxItems":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "7060902010", "url": "https://www.autovit.ro/autoturisme/anunt/dacia-sandero-ver-1-4-mpi-ID7HQPai.html", "make": "dacia", "year": 2009, "model": "sandero", "title": "Dacia Sandero 1.4 MPI", "photos": [ "https://ireland.apollo.olxcdn.com/v1/files/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmbiI6ImVqM3hjY3VxMGJ1Zi1BVVRPVklUUk8ifQ.lEbda1pyIeWplmwPKWoKvcP_hL4MxfN3lKTjdzfEh1U/image", "https://ireland.apollo.olxcdn.com/v1/files/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmbiI6ImhnNGp3ODJsamw2Zi1BVVRPVklUUk8ifQ.uGfTLlf5tWh83IdB5dQb3bS7gxQAMa112F6ThvtvKjc/image" ], "gearbox": "manual", "mileage": 160000, "currency": "EUR", "fuel_type": "petrol", "seller_id": "4708002", "created_at": "2026-09-26T06:23:27Z", "photo_count": 8, "price_value": 1350, "seller_name": "Example Auto SRL", "seller_type": "PROFESSIONAL", "location_city": "Valea Nandrii", "location_region": "Arges" } ``` # mobile.de listings Source: https://docs.usegoro.ai/tools/automotive/mobilede-listings Car listings from mobile.de, Germany's largest car marketplace, from a search URL or from make, model, price, year, mileage and location filters. Every listing Car listings from mobile.de, Germany's largest car marketplace, from a search URL or from make, model, price, year, mileage and location filters. Every listing comes back with price, mileage, first registration, fuel, power, the ad URL, how many photos it has with the photo URLs, and the seller: dealer or private, name, city, rating, review count, how many cars they list, and the phone numbers, website and email the ad shows. Use to find car sellers on mobile.de and qualify them in one call: which dealers carry a given make and price band in an area, how big their stock is, and which ads have few or weak photos (numImagesAvailable, imageUrls), the classic lead list for anyone selling dealers photography, detailing, financing or software. Paste a mobile.de search URL in startUrls to reuse any filter the site offers, or use the structured filters. maxResults sets both how many listings come back and what you pay: one billed row per listing, plus a small fixed start fee per call, so batch a whole search into one call instead of many small ones. Use autoscout24.listings for AutoScout24 (Germany and eight other EU markets) and autovit.listings for Romania. ## Price **\$0.001770 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ------------ | ---------------------------------------------------------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `make` | `string` | | Make as mobile.de names it, e.g. Volkswagen, BMW, Mercedes-Benz. | | `sort` | `relevance`, `price_asc`, `price_desc`, `mileage_asc`, `mileage_desc`, `registration_desc`, `registration_asc`, `newest`, `oldest` | | Order of results. newest surfaces freshly posted ads first. Defaults to `"relevance"`. | | `model` | `string` | | Model name, e.g. Golf. Requires make. | | `yearMax` | `integer` | | Latest first registration year. | | `yearMin` | `integer` | | Earliest first registration year. | | `zipCode` | `string` | | German postal code to search around. Pair with radiusKm. | | `priceMax` | `integer` | | Maximum price in EUR. | | `priceMin` | `integer` | | Minimum price in EUR. | | `radiusKm` | `integer` | | Search radius in km around zipCode. | | `condition` | `array` | | Vehicle conditions to include. | | `startUrls` | `array` | | mobile.de SEARCH result URLs, copied from the browser (suchen.mobile.de/fahrzeuge/search.html?...). Carries every filter the site offers. When set, the structured filters below are not needed. Search pages only: a single ad URL returns nothing. | | `maxResults` | `integer` | | How many listings to return, and the number you pay for: one billed row per listing. Capped at 500 per call. Defaults to `50`. | | `mileageMax` | `integer` | | Maximum mileage in km. | | `sellerType` | `DEALER`, `PRIVATE` | | Only dealers or only private sellers. Leave out for both. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"mobilede.listings","input":{"maxResults":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "make": "Volkswagen", "model": "Golf", "price": 17670, "title": "Volkswagen Golf", "powerKw": 180, "fuelType": "PETROL", "imageUrls": [ "https://m.mobile.de/yams-proxy/img.classistatic.de/api/v1/mo-prod/images/27/27183272-2ab9-4495-897a-776ecfec438a", "https://m.mobile.de/yams-proxy/img.classistatic.de/api/v1/mo-prod/images/88/8861e411-ff7d-48f9-8f10-291ae7444f93" ], "listingId": "dbe907cc1e2583bba37a6688386e884657eb3215768085d29b54bdfc6f124e14", "mileageKm": 103319, "sellerName": "Example Autohaus GmbH", "sellerType": "Dealer", "firstSeenAt": null, "canonicalUrl": "https://suchen.mobile.de/auto-inserat/volkswagen-golf-vii-2-0-tsi-gti-performance-bmt-led-acc-pdc-neufahrn-b-freising/461430737.html", "sellerEmails": [ "info@example-dealer.de" ], "sellerPhones": [ "+49 000 0000000" ], "sellerRating": 4.4, "sellerCountry": "DE", "sellerWebsite": "https://www.example-dealer.de", "sellerZipCity": null, "firstRegistration": "08/2017", "sellerReviewCount": 87, "numImagesAvailable": 31, "sellerListingsCount": null } ``` # Amazon product details Source: https://docs.usegoro.ai/tools/ecommerce/amazon-product The full detail page for specific Amazon ASINs or product URLs: title, brand, price and list price, rating count and star distribution, availability, bullet poi The full detail page for specific Amazon ASINs or product URLs: title, brand, price and list price, rating count and star distribution, availability, bullet points, long description, best sellers rank, breadcrumbs, seller, model number and image URLs. Use when you already have ASINs or Amazon product URLs and need the whole listing: pricing, specs, ranking and imagery. Run amazon.search first if you still need to find the ASINs. ## Price **\$0.0045 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | -------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Params` | `array` | yes | Amazon ASINs or full product URLs, for example B077Z99YGY or [https://www.amazon.com/dp/B077Z99YGY](https://www.amazon.com/dp/B077Z99YGY). Mixing both forms is fine. Exactly one billed result per entry, so cost scales with the length of this array. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"amazon.product","input":{"Params":["example"]}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "asin": "B07CMS5Q6P", "price": "$39.99", "title": "Logitech G305 Lightspeed Wireless Gaming Mouse - Black | Hero Sensor 12, Lightspeed 1ms Wireless, Up to 250h on 1xaa, 6 Programmable Buttons, 99g with Battery", "images": [ "https://m.media-amazon.com/images/I/31zy93Vq31L.jpg", "https://m.media-amazon.com/images/I/41jBvXcD7eL.jpg" ], "about_item": "The next-generation optical HERO sensor delivers incredible performance and up to 10x the power efficiency over previous generations.", "brand_name": "Logitech G", "list_price": "$49.99", "breadcrumbs": "Video Games > PC > Accessories > Gaming Mice", "product_url": "https://www.amazon.com/dp/B07CMS5Q6P", "scrape_time": "2026-07-30 22:36", "seller_name": "Amazon.com", "availability": "In Stock", "manufacturer": "Logitech", "model_number": "910-005280", "rating_count": "39211", "rating_stars": "4.6 out of 5 stars", "delivery_date": "", "brand_page_url": "", "default_variant": [ "style:Classic", "color:Black" ], "seller_page_url": "", "recent_purchases": "10K+ bought", "best_sellers_rank": "#7 in Video Games #1 in PC Gaming Mice #1 in Mac Gaming Mice", "product_description": "Play advanced without wires or limits. The Logitech G305 LIGHTSPEED is a gaming wireless mouse designed for competition.", "rating_distribution": { "1": "2%", "2": "2%", "3": "5%", "4": "13%", "5": "78%" }, "fastest_delivery_date": "", "customer_review_summary": "" } ``` # Amazon product reviews Source: https://docs.usegoro.ai/tools/ecommerce/amazon-reviews Customer reviews for one or more Amazon ASINs, with rating, title, body text, author name, date, verified purchase and Vine flags, helpful votes, attached image Customer reviews for one or more Amazon ASINs, with rating, title, body text, author name, date, verified purchase and Vine flags, helpful votes, attached image and video URLs, plus the product overall rating and its star distribution. Use to read what buyers actually say about a specific Amazon product, to mine complaints and feature requests, or to track review sentiment over time. This is the high volume reviews tool. Use amazon.reviews\_global when you need non US Amazon domains or star, keyword and date filters. ## Price **\$0.0027 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------- | | `input` | `array` | yes | One entry per ASIN. Every review returned is billed, so each extra entry and each extra page multiplies the cost. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"amazon.reviews","input":{"input":["example"]}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "asin": "B07CMS5Q6P", "date": "Reviewed in the United States on July 27, 2026", "text": "Battery life is genuinely months, not weeks. Tracking is flawless on a cloth pad.", "vine": false, "title": "Excellent mouse for the price", "rating": "5.0 out of 5 stars", "reviewId": "R2216I3XRQQT5A", "userName": "J. Fischer", "verified": true, "domainCode": "com", "statusCode": 200, "currentPage": 1, "profilePath": "/gp/profile/amzn1.account.EXAMPLE", "countRatings": 39211, "countReviews": 4210, "imageUrlList": [], "productTitle": "Logitech G305 Lightspeed Wireless Gaming Mouse", "sortStrategy": "recent", "videoUrlList": [], "productRating": "4.6 out of 5 stars", "reviewSummary": { "oneStar": { "percentage": 2 }, "twoStar": { "percentage": 2 }, "fiveStar": { "percentage": 78 }, "fourStar": { "percentage": 13 }, "threeStar": { "percentage": 5 } }, "statusMessage": "success", "variationList": [ "Style: Classic", "Color: Black" ], "numberOfHelpful": 3 } ``` # Amazon reviews (global) Source: https://docs.usegoro.ai/tools/ecommerce/amazon-reviews_global Amazon customer reviews across more than 20 regional domains, with star, keyword, media, date, language and verified purchase filters. Each review carries ratin Amazon customer reviews across more than 20 regional domains, with star, keyword, media, date, language and verified purchase filters. Each review carries rating, title, body, date, country, product variant, helpful votes, and any attached images or videos. Use for Amazon reviews outside the US, or when you need star, keyword, language or date filters, or reviews collected across every product variant. Use amazon.reviews for plain high volume pulls on one domain. ## Price **\$0.0027 per review.** That is the rate you pay for each review the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer reviews costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | `sort` | `helpful`, `recent` | | Defaults to `"helpful"`. | | `limit` | `integer` | | COSTS EXTRA. Review pages per product, 10 reviews each. Amazon itself stops at 10 pages (100 reviews) per product per filter. Defaults to `10`. | | `stars` | `array` | | Scrape several specific star ratings. | | `rating` | `all`, `five_star`, `four_star`, `three_star`, `two_star`, `one_star`, `positive`, `critical` | | Filter reviews by star rating. Defaults to `"all"`. | | `region` | `amazon.com`, `amazon.ca`, `amazon.de`, `amazon.fr`, `amazon.co.uk`, `amazon.it`, `amazon.es`, `amazon.com.au`, `amazon.co.jp`, `amazon.com.br`, `amazon.com.mx`, `amazon.nl`, `amazon.ie`, `amazon.se`, `amazon.com.tr`, `amazon.ae`, `amazon.sg`, `amazon.sa`, `amazon.pl`, `amazon.com.be`, `amazon.eg`, `amazon.in` | | Amazon domain the ASINs belong to. Defaults to `"amazon.com"`. | | `end_date` | `string` | | Inclusive end of the date range, YYYY-MM-DD. | | `keywords` | `array` | | Only reviews containing these keywords. | | `language` | `all`, `en`, `es`, `fr`, `de`, `pt`, `it`, `nl`, `pl`, `sv`, `cs`, `zh_CN`, `zh_TW`, `ja`, `ko`, `ar`, `tr` | | Defaults to `"all"`. | | `products` | `array` | yes | Amazon ASINs or product review URLs. Cost scales with this array: every review of every product is billed. | | `all_stars` | `boolean` | | COSTS EXTRA, up to 5x. Scrapes up to 100 reviews per star rating, so up to 500 billed reviews per product instead of 100. Defaults to `false`. | | `start_date` | `string` | | Inclusive start of the date range, YYYY-MM-DD. | | `avp_reviews` | `boolean` | | Only reviews with the verified purchase badge. Defaults to `false`. | | `personal_data` | `boolean` | | Include reviewer names, profile ids and photos. Personal data under GDPR and CCPA, so only enable it if you have a lawful basis. Defaults to `false`. | | `include_variants` | `boolean` | | COSTS EXTRA. Include reviews written for other variants of the product, which raises the number of billed reviews. Defaults to `false`. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"amazon.reviews_global","input":{"products":["example"]}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "images": [], "rating": 3, "videos": [], "aspects": [], "country": "US", "language": "en", "reviewId": "R2216I3XRQQT5A", "profileId": null, "reviewUrl": "https://www.amazon.com/review/R2216I3XRQQT5A", "scrapedAt": "2026-07-30T14:32:20.346000+00:00", "productUrl": "https://www.amazon.com/dp/B07MVJZQTC", "reviewDate": "2026-07-27", "reviewText": "Smells great but doesn't help with texturing or volume", "reviewedIn": "Reviewed in the United States on July 27, 2026", "sellerName": null, "vineReview": false, "productAsin": "B07MVJZQTC", "profileName": null, "reviewTitle": "Does not give the desired result", "variantAsin": "B07MVJZQTC", "productTitle": null, "profilePhoto": null, "totalRatings": null, "variantSpecs": [ "Scent: Ocean Waves - Tropical Coconut", "Size: 6 Fl Oz (Pack of 1)" ], "averageRating": null, "ratingSummary": null, "helpfulVoteCount": 0, "reviewsAISummary": null, "sellerProfileUrl": null, "verifiedPurchase": true } ``` # Amazon search results Source: https://docs.usegoro.ai/tools/ecommerce/amazon-search Amazon search result pages for one or more keywords. Each product carries its ASIN, title, price and retail price, rating and review count, Prime and sponsored Amazon search result pages for one or more keywords. Each product carries its ASIN, title, price and retail price, rating and review count, Prime and sponsored flags, image and product URL, and the position it held in the results. Use to find products on Amazon by keyword, to see where a listing ranks for a search term, or to pull a competitive set with prices and ratings. Use amazon.product instead when you already know the ASINs. ## Price **\$0.0003 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- | | `input` | `array` | yes | One entry per keyword search. Every product returned is billed, so each extra entry and each extra page multiplies the cost. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"amazon.search","input":{"input":["example"]}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "asin": "B07CMS5Q6P", "dpUrl": "https://www.amazon.com/dp/B07CMS5Q6P", "price": 39.99, "prime": true, "imgUrl": "https://m.media-amazon.com/images/I/31zy93Vq31L.jpg", "keyword": "wireless mouse", "sponsored": false, "domainCode": "com", "statusCode": 200, "countReview": 39211, "currentPage": 1, "resultCount": 2000, "retailPrice": 49.99, "salesVolume": "10K+ bought in past month", "sortStrategy": "relevanceblender", "productRating": "4.6 out of 5 stars", "statusMessage": "success", "productDescription": "Logitech G305 Lightspeed Wireless Gaming Mouse", "searchResultPosition": 1 } ``` # Amazon seller leads Source: https://docs.usegoro.ai/tools/ecommerce/amazon-sellers_eu Rows from a prebuilt database of Amazon marketplace sellers: business name and type, company registration and VAT number, published contact details, business ad Rows from a prebuilt database of Amazon marketplace sellers: business name and type, company registration and VAT number, published contact details, business address and country, seller ratings and review counts over lifetime, one year and three month windows, plus one example ASIN and its category. Use to build a B2B prospect list of Amazon sellers filtered by product category, country of residence and discovery date. Every returned row is billed and this is the most expensive endpoint in the catalog per row, so set max\_results to what you will actually use and page with offset. The rows are business contact data, so confirm your use is lawful under GDPR and local marketing rules before you contact anyone. ## Price **\$0.0267 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------ | | `offset` | `integer` | | Rows to skip, for paging past leads you already pulled. Defaults to `1`. | | `date_added` | `string` | | Only leads discovered after this date, YYYY-MM-DD. | | `max_results` | `integer` | | COSTS EXTRA, linearly. Rows to return. Every row is billed. Defaults to `100`. | | `seller_country` | `US`, `CN`, `DE`, `AT`, `AU`, `BE`, `BG`, `CA`, `CH`, `CY`, `CZ`, `DK`, `EE`, `ES`, `FI`, `FR`, `GB`, `GR`, `HK`, `HU`, `IE`, `IN`, `IT`, `JP`, `KR`, `LT`, `LV`, `NL`, `PL`, `PT`, `RO`, `SE`, `SI`, `SK`, `TR`, `TW` | | Seller country of residence. | | `amazon_category` | `Automotive`, `Baby Products`, `Beauty`, `Books`, `Clothing, Shoes & Jewelry`, `Business, Industry & Science`, `Computers`, `DIY & Tools`, `Electronics`, `Fashion`, `Garden`, `Grocery`, `Handmade Products`, `Health & Personal Care`, `Health & Household`, `Home & Kitchen`, `Industrial & Scientific`, `Large Appliances`, `Lighting`, `Musical Instruments & DJ`, `Office Products`, `PC & Video Games`, `Pet Supplies`, `Sports & Outdoors`, `Stationery & Office Supplies`, `Tools`, `Toys` | | Amazon category the seller sells in. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"amazon.sellers_eu","input":{"max_results":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "asin": "B0CH9VFS1J", "email": "contact@example.com", "category": "Fashion", "seller_id": "sample-id-000", "vat_number": null, "about_seller": "", "country_code": "CN", "phone_number": "+1 555 0100", "product_name": "Pearl Cross Baroque Brooches for Women", "amazon_market": "amazon.de", "business_name": "Example Trading Co Ltd", "business_type": "Privately-owned business", "rating_1_year": 4.2, "discovery_date": "2026-05-18", "reviews_1_year": 6, "rating_3_months": 4.5, "rating_lifetime": 4.5, "seller_page_url": "https://www.amazon.de/sp?ie=UTF8&seller=A100L9JLD7KMS7", "business_address": "12 Example Road, Example City, 350000", "reviews_3_months": 2, "reviews_lifetime": 14, "registration_number": "91350103MAC6KXLD3N", "multiple_sellers_for_asin": false } ``` # App Store reviews Source: https://docs.usegoro.ai/tools/ecommerce/appstore-reviews Customer reviews for the Apple App Store apps you name: star rating, review title and body, the reviewer's display name and profile link, the app version that w Customer reviews for the Apple App Store apps you name: star rating, review title and body, the reviewer's display name and profile link, the app version that was reviewed, the review date and the storefront it came from. One result, and one billable item, per review. Apple serves at most 500 reviews per app per storefront, so run several countries to go wider. Use when you need what users actually say about an iOS app: competitor research, sentiment and theme analysis, checking whether a release answered the complaint people kept posting, or pulling your own reviews to reply to them. Give the numeric App Store id, which is the digits after /id in a store URL such as apps.apple.com/us/app/name/id1234567890. ## Price **\$0.0003 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ----------- | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `appIds` | `array` | yes | Numeric App Store ids, without the id prefix. Reviews from every app named here share the one maxItems budget, they do not each get their own. | | `country` | `string` | | App Store storefront to read reviews from, as a LOWERCASE ISO 3166-1 alpha-2 code (us, gb, de, jp). Uppercase is rejected upstream. Reviews are storefront specific, so this changes which reviews exist, not just their order. Defaults to `"us"`. | | `maxItems` | `integer` | | Total reviews to return across every app and every start URL in this call. This is what the call is priced on, one billable item per review, so it is the only number that moves the cost. Capped at 5000 because that is the most Goro will hand back from one run. Defaults to `200`. | | `startUrls` | `array` | | Optional App Store app URLs to scrape in the same run, in addition to appIds. Use this when you have store links rather than ids. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"appstore.reviews","input":{"appIds":["284882215","1234567890"],"maxItems":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "11223344556", "url": "https://itunes.apple.com/us/review?id=1234567890&type=Purple%20Software", "date": "2026-07-18T09:41:22-07:00", "text": "Invented sample text. The widget is the reason I kept it after the trial. Sync broke once after an update and support had it fixed the same day.", "appId": "1234567890", "score": 4, "title": "Almost exactly what I wanted", "country": "US", "userUrl": "https://itunes.apple.com/us/reviews/id111222333", "version": "4.12.0", "userName": "harbourline", "inputSource": "1234567890" } ``` # App Store keyword search Source: https://docs.usegoro.ai/tools/ecommerce/appstore-search Ranked Apple App Store search results for the keywords you name: each app's position for that keyword, its name, numeric App Store id, developer, genre, price, Ranked Apple App Store search results for the keywords you name: each app's position for that keyword, its name, numeric App Store id, developer, genre, price, star rating, rating count, release date, icon and store link. Every row also carries keyword level market context (how many apps compete for the term, the top ten's average rating and median rating count, how many of the top ten are paid) and three 0 to 100 estimates for popularity, ranking difficulty and opportunity. One result, and one billable item, per app per keyword. Use to find the apps that own a search term before you build against them or position around them, to size how contested a keyword is, or to pull a competitor shortlist you then feed into appstore.reviews. Returns up to 20 apps per keyword per storefront, across 60 country storefronts. Apple only; see the stores field. ## Price **\$0.0030 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `topN` | `integer` | | How many top ranked apps to return per keyword. Multiplies the number of results, and therefore the cost, directly. Defaults to `10`. | | `action` | `keyword-top-apps` | | Fixed. The underlying source can also expand a seed keyword into suggestions and track a named app's rank, but those count their results from different fields, so they would break this endpoint's price estimate and will get their own endpoints instead. Defaults to `"keyword-top-apps"`. | | `stores` | `array` | | Which store to search. Leave it alone unless you have a reason: google returns zero rows through this endpoint, because Google Play needs a residential proxy for this source and caller supplied proxy configuration is refused. Asking for google costs nothing, since no rows means no billable items, but it returns nothing either. Defaults to `["apple"]`. | | `keywords` | `array` | yes | Search terms to rank apps for. Each keyword returns its own set of up to topN apps, and every app returned is one billable item, so the cost of a call is keywords x topN. | | `storefront` | `us`, `gb`, `jp`, `kr`, `cn`, `de`, `fr`, `au`, `ca`, `br`, `in`, `mx`, `it`, `es`, `nl`, `se`, `no`, `dk`, `fi`, `ch`, `at`, `be`, `pt`, `ie`, `nz`, `sg`, `hk`, `tw`, `th`, `my`, `ph`, `id`, `vn`, `sa`, `ae`, `il`, `tr`, `ru`, `pl`, `cz`, `hu`, `ro`, `bg`, `hr`, `sk`, `si`, `lt`, `lv`, `ee`, `gr`, `ua`, `za`, `ng`, `ke`, `eg`, `ar`, `cl`, `co`, `pe`, `pk` | | Country storefront to search, as a lowercase ISO 3166-1 alpha-2 code. Note gb, not uk. Defaults to `"us"`. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"appstore.search","input":{"keywords":["habit tracker","calorie counter"]}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "url": "https://apps.apple.com/us/app/lumen-habit-tracker/id1234567890?uo=4", "appId": "1234567890", "genre": "Health & Fitness", "price": "Free", "store": "apple", "action": "keyword-top-apps", "rating": 4.68312, "appName": "Lumen Habit Tracker", "appRank": 1, "iconUrl": "https://example.com/media/sample-app-icon.png", "keyword": "habit tracker", "developer": "Northgate Labs", "scrapedAt": "2026-08-04T11:14:27.797Z", "storefront": "us", "ratingCount": 21847, "releaseDate": "2021-04-16", "totalResults": 183, "top10AvgRating": 4.61, "top10PaidCount": 1, "difficultyEstimate": 61, "popularityEstimate": 74, "top10MedianRatings": 48210, "opportunityEstimate": 31 } ``` # Google Play reviews Source: https://docs.usegoro.ai/tools/ecommerce/playstore-reviews Customer reviews for the Google Play apps you name: star rating, review body, the reviewer's display name and avatar, the app version that was reviewed, thumbs Customer reviews for the Google Play apps you name: star rating, review body, the reviewer's display name and avatar, the app version that was reviewed, thumbs up count, the developer's reply where there is one, and Google's per feature star breakdown. One result, and one billable item, per review. Google Play groups reviews by the language they were written in, so language is what decides which reviews you get. Use when you need what users actually say about an Android app: competitor research, sentiment and theme analysis, spotting the version a rating slide started at, or pulling your own reviews and the replies you already sent. Give the package name, which is the value after ?id= in a play.google.com URL, such as com.instagram.android. ## Price **\$0.0003 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ----------- | --------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `sort` | `NEWEST`, `RATING`, `HELPFULNESS` | | Which reviews to take first when maxItems is smaller than what the app has. NEWEST for monitoring, RATING to reach the low stars, HELPFULNESS for the ones other users voted up. Defaults to `"NEWEST"`. | | `appIds` | `array` | yes | Google Play package names. Reviews from every app named here share the one maxItems budget, they do not each get their own. | | `country` | `string` | | Google Play storefront to read the app from, as an UPPERCASE ISO 3166-1 alpha-2 code (US, GB, DE, BR). Lowercase is rejected upstream. Use language, not this, to change which reviews come back. Defaults to `"US"`. | | `language` | `string` | | Language of the reviews to return, as a Google Play language code (en, de, fr, pt-BR, es-419). Google Play buckets reviews by the language they were written in, so leaving this at en returns only English reviews however large the app is. Defaults to `"en"`. | | `maxItems` | `integer` | | Total reviews to return across every app and every start URL in this call. This is what the call is priced on, one billable item per review, so it is the only number that moves the cost. Capped at 5000 because that is the most Goro will hand back from one run. Defaults to `200`. | | `startUrls` | `array` | | Optional Google Play app URLs to scrape in the same run, in addition to appIds. Use this when you have store links rather than package names. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"playstore.reviews","input":{"appIds":["com.instagram.android","org.telegram.messenger"],"maxItems":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "0f2b7c14-5a63-4d8e-9f01-2c7a5b3d6e84", "url": "https://play.google.com/store/apps/details?id=com.example.lumen&reviewId=0f2b7c14-5a63-4d8e-9f01-2c7a5b3d6e84", "date": "2026-07-19T08:12:44.512Z", "text": "Invented sample text. Good tracker and the import worked first time, but the weekly summary stopped arriving after the last update.", "appId": "com.example.lumen", "score": 3, "title": null, "country": "US", "version": "4.12.0", "language": "en", "thumbsUp": 6, "userName": "R. Okafor", "criterias": [ { "rating": null, "criteria": "vaf_app_quality_stability" }, { "rating": 4, "criteria": "vaf_phase1_ease_of_use" } ], "replyDate": "2026-07-20T10:02:11.004Z", "replyText": "Invented sample reply. That is fixed in 4.12.1, thanks for flagging it.", "scoreText": "3", "userImage": "https://example.com/media/sample-avatar.png", "inputSource": "com.example.lumen" } ``` # Google Shopping search Source: https://docs.usegoro.ai/tools/ecommerce/shopping-search Live Google Shopping listings for a query: product title, merchant, price, product id, image URL, the listing link and the position it held, localized by countr Live Google Shopping listings for a query: product title, merchant, price, product id, image URL, the listing link and the position it held, localized by country and language, with pagination. Use to compare prices for a product across merchants, to watch how a listing ranks on Google Shopping, or to snapshot what a category sells for in a given country. Use amazon.search when you only care about Amazon. ## Price **\$0.0105 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ------------ | --------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | `num` | `10`, `20`, `30`, `40`, `50`, `100` | | COSTS EXTRA. Results requested per page. Google returns roughly double this and stops near 40, and every returned listing is billed. Defaults to `"10"`. | | `query` | `string` | yes | The product or category to search for, for example "running shoes". | | `country` | `string` | | Two letter country code to search in, for example us, gb, de, fr, ro. Defaults to `"us"`. | | `queries` | `array` | | Overrides query when present. Limited to one entry: send one call per query so the price stays proportional to the request. | | `language` | `string` | | Two letter language code, for example en, de, fr, es, ro. Defaults to `"en"`. | | `max_pages` | `integer` | | COSTS EXTRA, linearly. Pages to scrape. This is the field the price is sized on. Defaults to `1`. | | `date_range` | `anytime`, `qdr:h`, `qdr:d`, `qdr:w`, `qdr:m` | | Restrict to listings seen in the last hour, day, week or month. Defaults to `"anytime"`. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"shopping.search","input":{"query":"example","max_pages":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "link": "https://www.google.com/search?ibp=oshop&q=running+shoes&prds=productid:16078491969622180360&gl=us&udm=28", "price": "$150.00", "query": "running shoes", "title": "Nike Air Max 270 Men's Shoes", "source": "Nike.com", "imageUrl": "https://example.com/media/sample.jpg", "position": 1, "productId": "16078491969622180360" } ``` # Company fundamentals and analyst view Source: https://docs.usegoro.ai/tools/finance/finance-fundamentals Two views of a company, chosen with operation. fundamentals returns the financial statements as numeric fields, one row per reporting period, annual and quarter Two views of a company, chosen with operation. fundamentals returns the financial statements as numeric fields, one row per reporting period, annual and quarterly, about ten periods per company: total revenue, cost of revenue, gross profit, operating expense, R\&D, SG\&A, operating income, EBIT, EBITDA, net income, basic and diluted EPS, total assets, current assets and liabilities, inventory, receivables, cash, total and long term debt, stockholders equity, retained earnings, working capital, operating, investing and financing cash flow, capital expenditure, dividends paid and free cash flow. analysis returns one row per company with the analyst picture: recommendation key and mean score, the number of opinions, current price, mean, median, high and low price targets, the recommendation trend month by month, EPS and revenue estimate trends, earnings history with surprises, the upgrade and downgrade log, the index trend and Yahoo research insights. Use for the numbers behind a company rather than its price: comparing margins, leverage or cash generation across a peer set, tracking revenue and EPS quarter by quarter, or reading what analysts currently expect and how that has moved. Set operation first, because the two answer different questions and one call returns one of them. Symbols are Yahoo Finance tickers, up to 20 per call. Funds and ETFs file no statements, so fundamentals returns no rows for SPY and the like and the call is not billed for them. Use finance.quote when a single summary row per symbol is enough, finance.screener to build the peer set in the first place, and news.search for the coverage around a result. Figures come from Yahoo Finance's public feeds rather than from a filings service, so they can trail an official filing, they can be restated later, and coverage thins outside large listed companies. ## Price **\$0.0030 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ----------- | -------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `tickers` | `array` | yes | Yahoo Finance symbols, or full quote URLs. Statements exist for operating companies, so ETFs, funds, indices, crypto and forex return no rows for fundamentals. Up to ten billable rows per symbol for fundamentals, exactly one for analysis. | | `maxItems` | `integer` | | Optional hard ceiling on rows for the whole run, filled in symbol order. Useful as a spend guard on a long list. Leave it out and every period of every symbol is returned. | | `operation` | `fundamentals`, `analysis` | yes | Which view you want. fundamentals returns the statements, about ten period rows per company. analysis returns one row per company with ratings, price targets and estimate trends. Required, with no default on purpose: the underlying source falls back to live quotes when this is missing, so a call that meant to ask for statements would be answered, and billed, with snapshots. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"finance.fundamentals","input":{"operation":"fundamentals","tickers":["AAPL","MSFT"]}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "eBIT": 5177000000, "eBITDA": 6042000000, "period": "annual", "symbol": "NRTH", "endDate": "2025-12-31", "basicEPS": 9.51, "currency": "USD", "inventory": 412000000, "netIncome": 3949000000, "totalDebt": 5444000000, "dilutedEPS": 9.44, "grossProfit": 9615000000, "totalAssets": 27140000000, "freeCashFlow": 3216000000, "longTermDebt": 4720000000, "totalRevenue": 18422000000, "costOfRevenue": 8807000000, "currentAssets": 11208000000, "workingCapital": 4306000000, "operatingIncome": 5177000000, "operatingExpense": 4438000000, "retainedEarnings": 2894000000, "cashDividendsPaid": -401000000, "financingCashFlow": -2740000000, "investingCashFlow": -1188000000, "operatingCashFlow": 4402000000, "accountsReceivable": 3106000000, "capitalExpenditure": -1186000000, "currentLiabilities": 6902000000, "stockholdersEquity": 7351000000, "cashAndCashEquivalents": 4180000000, "researchAndDevelopment": 2611000000, "sellingGeneralAndAdministration": 1827000000, "totalLiabilitiesNetMinorityInterest": 19789000000 } ``` # Historical price candles Source: https://docs.usegoro.ai/tools/finance/finance-history OHLCV candles for the symbols you name, one row per candle: symbol, date, open, high, low, close, adjusted close, volume, the interval it was cut at, the curren OHLCV candles for the symbols you name, one row per candle: symbol, date, open, high, low, close, adjusted close, volume, the interval it was cut at, the currency and the exchange. Intervals run from 1 minute to 3 months, and the window is either a named range from 1 day to max or an explicit start and end date. Turning on events adds a row per dividend, with its amount, and a row per stock split, with its ratio. Works for stocks, ETFs, crypto pairs, forex pairs, indices and futures. One result, and one billable item, per row. Use when you need the series rather than a single snapshot: backtests, drawdown and volatility maths, correlations across symbols, charting, or total return work that needs dividends and splits. Rows here are candles, not symbols, so a year of daily data is about 252 rows per symbol and maxItems is the ceiling you set on the whole call. Two things follow from that and both are worth knowing before the first call: the ceiling is filled in symbol order, so a call that runs out of room returns nothing at all for the later symbols, which makes one symbol per call the right shape for a long series; and intraday intervals only reach as far back as Yahoo keeps them, roughly 30 days at 1 minute and 2 years at 1 hour, so an old window at a fine interval comes back short or empty. Use finance.quote for where a symbol stands right now and finance.screener to pick the symbols first. Figures come from Yahoo Finance's public feeds rather than from an exchange, so the most recent candle can be delayed or still forming. ## Price **\$0.0030 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | --------------- | ----------------------------------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `range` | `1d`, `5d`, `1mo`, `3mo`, `6mo`, `1y`, `2y`, `5y`, `10y`, `ytd`, `max` | | How far back to fetch, relative to today. The source uses 1mo when you leave it out, and ignores this entirely when startDate is set. | | `endDate` | `string` | | Explicit window end, YYYY-MM-DD. Defaults to today, and is only read when startDate is set. | | `tickers` | `array` | yes | Yahoo Finance symbols, or full quote URLs. Stocks (AAPL), ETFs (SPY), crypto (BTC-USD), forex (EURUSD=X), indices (^GSPC) and futures (GC=F) all work. Capped at 10 because every symbol multiplies the candle count and the maxItems ceiling is shared across all of them. | | `interval` | `1m`, `2m`, `5m`, `15m`, `30m`, `60m`, `90m`, `1h`, `1d`, `5d`, `1wk`, `1mo`, `3mo` | | Candle size. The source uses 1d when you leave it out. Finer intervals mean more rows for the same window, so they cost proportionally more: a month at 1d is about 22 rows and the same month at 1h is about 150. Intraday sizes are only available for recent windows, roughly 30 days at 1m and 2 years at 1h. | | `maxItems` | `integer` | | Total rows to return across every symbol in this call, and the only number that moves the price: one billable item per row. The default of 250 is about a year of daily candles for one symbol. It is filled in symbol order rather than shared out, so a cap smaller than the request returns the first symbols complete and the later ones not at all, and the run can publish a few rows past the cap when several symbols are in flight. Defaults to `250`. | | `operation` | `historical` | | Fixed. The underlying source defaults to live quotes, so this is sent on every call: without it a request for candles would be answered, and billed, as snapshots. Defaults to `"historical"`. | | `startDate` | `string` | | Explicit window start, YYYY-MM-DD. Overrides range when set. | | `includeEvents` | `boolean` | | Also emit one row per dividend payment and one per stock split inside the window. Off unless you set it. Those rows carry type dividend or split instead of candle, and each is billed like any other row. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"finance.history","input":{"tickers":["AAPL"],"maxItems":10}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "low": 146.02000427246094, "date": "2026-08-06T13:30:00.000Z", "high": 149.11000061035156, "open": 146.90000915527344, "type": "candle", "close": 148.22000122070312, "symbol": "NRTH", "volume": 4182233, "adjClose": 148.22000122070312, "currency": "USD", "exchange": "NMS", "interval": "1d" } ``` # Options chains Source: https://docs.usegoro.ai/tools/finance/finance-options The option chain for the symbols you name, one row per contract: the underlying symbol and its price at capture, call or put, the OCC contract symbol, strike, e The option chain for the symbols you name, one row per contract: the underlying symbol and its price at capture, call or put, the OCC contract symbol, strike, expiration, last price, bid, ask, change and percent change, contract volume, open interest, implied volatility as a number, whether it is in the money, the currency and the last trade time. Covers as many upcoming expirations per symbol as you ask for. One result, and one billable item, per contract. Use for what the options market is currently pricing: implied volatility across strikes, open interest and unusual volume, put and call skew, or the quote on one specific contract. Rows here are contracts, not symbols, and a single expiration on a liquid US name runs 50 to 400 of them, so maxItems is the ceiling you set on the call. The chain is written calls first in ascending strike order, then puts, per expiration, which means a ceiling smaller than the chain returns low strike calls and no puts at all: ask for one symbol and one expiration when you want a chain you can reason about. Use finance.quote for the underlying snapshot and finance.history for its realised move. Figures come from Yahoo Finance's public feeds rather than from an exchange or an options data vendor, so quotes can be delayed and illiquid contracts can carry stale bids, zero volume and a last price from days ago. ## Price **\$0.0030 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | -------------------- | --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `tickers` | `array` | yes | Yahoo Finance symbols for instruments that have listed options, so equities, ETFs and indices. Capped at 10 because every symbol multiplies the contract count and the maxItems ceiling is shared across all of them. | | `maxItems` | `integer` | | Total contracts to return across every symbol and expiration in this call, and the only number that moves the price: one billable item per contract. 200 is roughly one liquid name's nearest expiration. Filled in chain order rather than shared out, so a cap smaller than the chain truncates the far strikes and the puts, and the run can publish a few rows past the cap when several symbols are in flight. Defaults to `200`. | | `operation` | `options` | | Fixed. The underlying source defaults to live quotes, so this is sent on every call: without it a request for a chain would be answered, and billed, as snapshots. Defaults to `"options"`. | | `optionsExpirations` | `integer` | | How many upcoming expiration dates to fetch per symbol, nearest first. Each expiration is a full call and put chain, so raising this multiplies the contracts, and therefore the cost, unless maxItems stops it first. Defaults to `1`. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"finance.options","input":{"tickers":["AAPL"],"maxItems":20}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "ask": 6.45, "bid": 6.2, "type": "call", "change": 0.4000001, "strike": 150, "symbol": "NRTH", "volume": 1842, "currency": "USD", "lastPrice": 6.35, "expiration": "2026-09-18T00:00:00.000Z", "inTheMoney": false, "openInterest": 9314, "lastTradeDate": "2026-08-07T19:52:11.000Z", "percentChange": 6.7226887, "contractSymbol": "NRTH260918C00150000", "underlyingPrice": 148.22, "impliedVolatility": 0.312255859375 } ``` # Stock and crypto quotes Source: https://docs.usegoro.ai/tools/finance/finance-quote A market snapshot per symbol, around 60 fields in one row: last price, change and percent change, day open, high and low, previous close, pre and post market pr A market snapshot per symbol, around 60 fields in one row: last price, change and percent change, day open, high and low, previous close, pre and post market prices, 52 week range, 50 and 200 day averages, volume and average volume, market cap and enterprise value, trailing and forward P/E, price to book, PEG, trailing and forward EPS, beta, dividend rate and yield, ex dividend date, payout ratio, shares outstanding and float, short ratio, revenue and revenue growth, gross, operating and profit margins, return on equity, cash, debt and free cash flow, the analyst consensus with mean and high and low price targets, and the company profile with sector, industry, country, headcount, website and a business summary. Works for stocks, ETFs, crypto pairs, forex pairs, indices and futures. One result, and one billable item, per symbol. Use when you already know the symbols and want the current price and where they stand right now: a price check, a watchlist refresh, a valuation and margin snapshot before a deeper pull, or the analyst consensus in the same row as the price. Symbols are Yahoo Finance tickers, so AAPL for a stock, SPY for an ETF, BTC-USD for crypto, EURUSD=X for forex, ^GSPC for an index and GC=F for futures, up to 50 per call. Use finance.screener to FIND symbols by filter or indicator rather than to look one up, finance.history for the price series behind the snapshot, finance.fundamentals for full statements and the analyst detail, news.search for coverage of a name, and trends.search for how much it is being searched for. Figures come from Yahoo Finance's public feeds rather than from an exchange, so prices can lag the tape and thinly covered symbols leave some fields empty. ## Price **\$0.0030 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ----------- | --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `tickers` | `array` | yes | Yahoo Finance symbols, or full quote URLs such as [https://finance.yahoo.com/quote/AAPL/](https://finance.yahoo.com/quote/AAPL/). Stocks (AAPL), ETFs (SPY), crypto (BTC-USD), forex (EURUSD=X), indices (^GSPC) and futures (GC=F) all work. Exactly one result, and one billable item, per symbol. A symbol Yahoo does not know is simply absent from the results and costs nothing. | | `maxItems` | `integer` | | Optional hard ceiling on rows for the whole run. Rows already equal symbols here, so this only matters as a spend guard on a long list. Leave it out and every symbol is returned. | | `operation` | `quotes` | | Fixed. The underlying source also returns candles, statements, analyst data, option chains, news and symbol search, and those count their rows from different fields, so they have their own endpoints rather than breaking this one's price estimate. Defaults to `"quotes"`. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"finance.quote","input":{"tickers":["AAPL","MSFT","BTC-USD"]}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "beta": 1.142, "sector": "Technology", "symbol": "NRTH", "country": "United States", "website": "https://example.com", "currency": "USD", "exchange": "NasdaqGS", "industry": "Software - Infrastructure", "longName": "Northgate Labs Incorporated", "pegRatio": 1.84, "forwardPE": 22.409605, "marketCap": 61840000000, "quoteType": "EQUITY", "shortName": "Northgate Labs Inc.", "totalCash": 4180000000, "totalDebt": 5444000000, "forwardEps": 6.62, "shortRatio": 1.94, "trailingPE": 27.114285, "floatShares": 402118344, "marketState": "REGULAR", "payoutRatio": 0.1755, "priceToBook": 8.412201, "trailingEps": 5.47, "dividendRate": 0.96, "freeCashflow": 3216000000, "grossMargins": 0.5218, "totalRevenue": 18422000000, "averageVolume": 5120433, "dividendYield": 0.0064999999, "profitMargins": 0.21440001, "revenueGrowth": 0.121, "exDividendDate": "2026-08-14T00:00:00.000Z", "preMarketPrice": 146.75, "returnOnEquity": 0.3312, "targetLowPrice": 121, "enterpriseValue": 63104000000, "fiftyDayAverage": 141.2284, "fiftyTwoWeekLow": 98.11, "postMarketPrice": 148.4, "targetHighPrice": 190, "targetMeanPrice": 162.4, "fiftyTwoWeekHigh": 163.4, "operatingMargins": 0.28104001, "fullTimeEmployees": 12400, "recommendationKey": "buy", "regularMarketOpen": 146.9, "regularMarketTime": "2026-08-07T20:00:01.000Z", "sharesOutstanding": 417200000, "recommendationMean": 1.9, "regularMarketPrice": 148.22, "averageVolume10days": 4783200, "longBusinessSummary": "Invented sample text. Northgate Labs Incorporated builds observability and data infrastructure software for enterprises. It sells through a direct sales force and cloud marketplaces, and was founded in 2004.", "regularMarketChange": 1.8399963, "regularMarketDayLow": 146.02, "regularMarketVolume": 4182233, "regularMarketDayHigh": 149.11, "twoHundredDayAverage": 129.71355, "numberOfAnalystOpinions": 27, "regularMarketChangePercent": 0.012569374, "regularMarketPreviousClose": 146.38 } ``` # Stock screener with technical indicators Source: https://docs.usegoro.ai/tools/finance/finance-screener Screens a market through TradingView and returns about 65 fields per stock in one row: symbol, exchange qualified symbol, name, exchange, sector, industry, coun Screens a market through TradingView and returns about 65 fields per stock in one row: symbol, exchange qualified symbol, name, exchange, sector, industry, country and currency; open, high, low, close, percent and absolute change, volume, 10 and 30 day average volume and relative volume; market cap, P/E, EPS, dividend yield, price to book, price to sales, return on equity and debt to equity; the analyst recommendation as a score from -1 to 1 with a strong buy to strong sell label, plus separate moving average and oscillator scores; RSI, MACD value and signal, stochastic K and D, CCI, ADX with both directional indicators, Williams %R, awesome oscillator, momentum and bull bear power; Bollinger band bounds, parabolic SAR, EMA and SMA at 10, 20, 50 and 200; weekly, monthly, three month, six month, yearly and year to date performance; monthly pivot support, middle and resistance, and daily volatility. Covers 14 markets including the US, UK, Germany, France, Japan, China, India, Korea, Australia, Canada, Brazil, crypto, forex and a global scan. One result, and one billable item, per stock returned. Use to FIND instruments rather than to look one up: the oversold large caps in a market by RSI, today's movers, a sector peer set ranked by market cap, or a full technical snapshot of a watchlist in one call. Either name symbols in TradingView form, NASDAQ:AAPL rather than AAPL, or leave them out and let market, minMarketCap, sortBy and sortOrder do the filtering. maxResults is the ceiling on how many stocks come back and it caps a named symbols list too, so it is the number that decides what a call returns and what it costs. Every row is one snapshot with the indicators computed on daily bars, so reach for finance.history when you need the series behind an indicator, finance.quote for the Yahoo view of a symbol you already have, and finance.fundamentals for full statements on a name this turns up. Figures come from TradingView's public scanner rather than from an exchange feed, so prices can be delayed, and coverage and field completeness vary by market. ## Price **\$0.15 per stock.** That is the rate you pay for each stock the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer stocks costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | -------------- | -------------------------------------------------------------------------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `market` | `america`, `brazil`, `uk`, `germany`, `france`, `japan`, `china`, `india`, `korea`, `australia`, `canada`, `crypto`, `forex`, `global` | yes | Which market to scan. america covers NASDAQ, NYSE and AMEX. crypto covers pairs across exchanges, forex covers currency pairs, and global scans everything at once. Required here even though the source defaults it to america, because that default answers a crypto or a London question with US equities and charges the same price for it. | | `sortBy` | `volume`, `market_cap_basic`, `change`, `Recommend.All`, `RSI`, `name` | | Which field decides who makes the cut, since maxResults takes the top of this ordering. Recommend.All is the analyst score, RSI with sortOrder asc surfaces the most oversold, change surfaces the movers, market\_cap\_basic the largest. Defaults to `"volume"`. | | `symbols` | `array` | | Scan only these symbols, in TradingView exchange qualified form such as NASDAQ:AAPL or NYSE:MSFT. Leave it out to scan the whole market through the filters instead. maxResults still applies, so a list longer than it is cut, and every symbol returned is one billable item. | | `sortOrder` | `asc`, `desc` | | Direction of that sort. desc for the largest, the most active or the biggest gainers, asc for the smallest, the most oversold or the biggest losers. Defaults to `"desc"`. | | `maxResults` | `integer` | | How many stocks to return, and the only number that moves the price: one billable item per stock. The source''s own default is 50; this endpoint sends 10 unless you say otherwise, so an unspecified call stays small. Capped at 50 per call, so a wider sweep is several calls with different filters. Defaults to `10`. | | `minMarketCap` | `integer` | | Only return stocks at or above this market capitalisation, in USD. The source uses 0, meaning no filter, when you leave it out. A useful way to keep a broad scan on names that are actually tradeable, for example 1000000000 for a billion. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"finance.screener","input":{"market":"america","maxResults":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "adx": 24.7712, "cci": 96.4417, "eps": 5.4665, "low": 146.02, "rsi": 61.4472, "high": 149.11, "name": "Northgate Labs Incorporated", "open": 146.9, "type": "stock", "close": 148.22, "ema10": 146.114, "ema50": 140.4471, "sma20": 143.8815, "sector": "Technology Services", "sma200": 129.7136, "stochD": 70.8104, "stochK": 74.2211, "symbol": "NRTH", "volume": 4182233, "bbLower": 136.4102, "bbUpper": 152.8841, "country": "United States", "peRatio": 27.1143, "perfYTD": 21.64, "currency": "USD", "exchange": "NASDAQ", "industry": "Packaged Software", "perfWeek": 2.11, "perfYear": 38.77, "platform": "tradingview", "macdValue": 1.8842, "marketCap": 61840000000, "perfMonth": 6.42, "scrapedAt": "2026-08-07T20:11:38.204Z", "fullSymbol": "NASDAQ:NRTH", "macdSignal": 1.5231, "perf3Months": 14.08, "pivotMiddle": 147.03, "priceToBook": 8.4122, "avgVolume10d": 4783200.4, "avgVolume30d": 5120433.1, "debtToEquity": 0.7406, "parabolicSAR": 141.2277, "priceToSales": 3.3568, "changePercent": 1.2569, "dividendYield": 0.6477, "pivotSupport1": 142.16, "changeAbsolute": 1.84, "relativeVolume": 0.8743, "returnOnEquity": 33.1204, "volatilityDaily": 1.9042, "pivotResistance1": 151.9, "recommendationMA": 0.6667, "recommendationAll": 0.4909, "recommendationLabel": "buy", "recommendationOscillators": 0.3151 } ``` # GPT Image 2 Source: https://docs.usegoro.ai/tools/image/image-gpt_image Generate an original image from a text prompt using OpenAI GPT Image 2, or EDIT images you supply. Send image_handles, image_urls or image_base64 to edit, resty Generate an original image from a text prompt using OpenAI GPT Image 2, or EDIT images you supply. Send image\_handles, image\_urls or image\_base64 to edit, restyle or combine up to 16 reference images; send none and it generates from the prompt alone. Editing costs exactly what generating costs at the same resolution. Returns the URL of the generated image. That URL is temporary: Goro holds a run's results for 24 hours and nothing guarantees the file outlives that, so collect what you need within 24 hours. Use for photorealism, clean product photography, sharp in-image text and design-ready compositions, or for edits that need to understand a brief rather than just a mask. The most instruction-faithful image model in the catalog. Pick 1K for drafts, 2K for finals, 4K when the image is going to print. ## Price **$0.10 per call at `2K`.** A flat price, and the only thing that changes it is `resolution`: `1K` costs **$0.06**, `2K` costs **$0.10**, `4K` costs **$0.16**. Every one of those is exact and known before the call, so the hold and the charge are the same number and there is no ceiling to reconcile. Omit `resolution` and you are billed the `2K` price. A call that fails, times out or is stopped releases its hold in full and costs nothing. ## Input | Field | Type | Required | Notes | | --------------- | ------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `prompt` | `string` | yes | What to draw, or what to change. Describe subject, composition, style, lighting and mood; the more specific, the closer the result. | | `image_urls` | `array` | | Optional. Public http or https links to the images to edit, combine or use as reference (JPEG, PNG or WEBP, up to 10 MB each, up to 16 of them). Use this when the images are ALREADY published somewhere; the output URL of any image endpoint works directly, which is what makes generate-then-edit a two-call sequence with nothing to upload in between. Nothing is copied or re-hosted, we pass the links on. For files that are not already public, use image\_handles instead and they never have to be published anywhere. Send one of the three, never two. | | `resolution` | `1K`, `2K`, `4K` | | Output resolution, and the only thing that changes the price: $0.06 at 1K, $0.10 at 2K, \$0.16 at 4K. Defaults to 2K. Defaults to `"2K"`. | | `aspect_ratio` | `auto`, `1:1`, `3:2`, `2:3`, `4:3`, `3:4`, `5:4`, `4:5`, `16:9`, `9:16`, `2:1`, `1:2`, `3:1`, `1:3`, `21:9`, `9:21` | | Shape of the output image. auto lets the model choose, or follows the reference images when editing. 5:4 and 4:5 only exist at 1K, and 1:1 does not exist at 4K. Defaults to `"auto"`. | | `image_base64` | `string` | | Optional, and only for ONE small image (under 3 MB) when you cannot run a shell command AND cannot ask the user to open a link. The image inline, as base64 of the file's own bytes; a data:image/png;base64, prefix is accepted and its declared type ignored. Only one image can arrive this way, because every inline image shares one request body. Understand the cost before choosing it: on an MCP call the request IS the tool arguments, which pass through the model's context window. A 3 MB photograph is 4 MB of base64, on the order of a million tokens, and in practice it is truncated and the run fails looking like file corruption. Do not downscale an image to fit here: use image\_handles, which takes up to 10 MB per file and keeps them out of the conversation entirely. JPEG, PNG or WEBP, checked against the file's own leading bytes rather than against any type you declare. Send one of the three, never two. | | `image_handles` | `array` | | Optional, and the RIGHT WAY to send local or private images to edit. Upload each file first with POST /v1/uploads (or the create\_upload MCP tool, which also hands back a link a PERSON can open to pick the file on their own device, for when you have no shell) and put the short handles it returns here, in order. The files go straight from you to a private bucket: they never pass through a tool call, so they never pass through a model's context window, and up to 10 MB each is fine. Nothing is published. A handle is usable for 10 minutes after its upload lands and the file is deleted 30 minutes after that. Supplying any image makes this an edit rather than a generation, at exactly the same price. Up to 16 images, and the extras are free. Send one of image\_handles, image\_urls and image\_base64, never two. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"image.gpt_image","input":{"prompt":"A minimalist product shot of a matte black water bottle on a concrete plinth, soft studio light."}}' ``` ## Response The full response. Values are illustrative. ```json theme={null} [ "https://example.com/goro/generated-image-1.png" ] ``` # Grok Imagine Image 2.0 Source: https://docs.usegoro.ai/tools/image/image-grok Generate an original image from a text prompt using xAI Grok Imagine Image 2.0, or EDIT images you supply. Send image_handles, image_urls or image_base64 to res Generate an original image from a text prompt using xAI Grok Imagine Image 2.0, or EDIT images you supply. Send image\_handles, image\_urls or image\_base64 to restyle, retouch or combine up to 5 reference images; send none and it generates from the prompt alone. Editing costs exactly what generating costs. Returns the URL of the generated image. That URL is temporary: Goro holds a run's results for 24 hours and nothing guarantees the file outlives that, so collect what you need within 24 hours. Use when you want a photographic or cinematic look at the lowest price in the catalog, or a precise edit of an image you already have: a change of wardrobe, a background swap, typography placed into a scene, a reference transformed into a new composition. Follows instructions closely and renders text well. For the hardest edits and the best text, use image.nano\_banana\_pro. ## Price **\$0.04 per call.** A flat price. Every call that succeeds costs exactly this, whatever it returns, and you know the number before you make the call. There is no ceiling to read and no usage to reconcile afterwards. A call that fails, times out or is stopped releases its hold in full and costs nothing. ## Input | Field | Type | Required | Notes | | --------------- | ----------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `prompt` | `string` | yes | What to draw, or what to change. Describe subject, composition, style, lighting and mood; the more specific, the closer the result. | | `image_urls` | `array` | | Optional. Public http or https links to the images to edit, combine or use as reference (JPEG, PNG or WEBP, up to 10 MB each, up to 5 of them). Use this when the images are ALREADY published somewhere; the output URL of any image endpoint works directly, which is what makes generate-then-edit a two-call sequence with nothing to upload in between. Nothing is copied or re-hosted, we pass the links on. For files that are not already public, use image\_handles instead and they never have to be published anywhere. Send one of the three, never two. | | `aspect_ratio` | `1:1`, `2:3`, `3:2`, `16:9`, `9:16` | | Shape of the output image. Defaults to `"1:1"`. | | `image_base64` | `string` | | Optional, and only for ONE small image (under 3 MB) when you cannot run a shell command AND cannot ask the user to open a link. The image inline, as base64 of the file's own bytes; a data:image/png;base64, prefix is accepted and its declared type ignored. Only one image can arrive this way, because every inline image shares one request body. Understand the cost before choosing it: on an MCP call the request IS the tool arguments, which pass through the model's context window. A 3 MB photograph is 4 MB of base64, on the order of a million tokens, and in practice it is truncated and the run fails looking like file corruption. Do not downscale an image to fit here: use image\_handles, which takes up to 10 MB per file and keeps them out of the conversation entirely. JPEG, PNG or WEBP, checked against the file's own leading bytes rather than against any type you declare. Send one of the three, never two. | | `image_handles` | `array` | | Optional, and the RIGHT WAY to send local or private images to edit. Upload each file first with POST /v1/uploads (or the create\_upload MCP tool, which also hands back a link a PERSON can open to pick the file on their own device, for when you have no shell) and put the short handles it returns here, in order. The files go straight from you to a private bucket: they never pass through a tool call, so they never pass through a model's context window, and up to 10 MB each is fine. Nothing is published. A handle is usable for 10 minutes after its upload lands and the file is deleted 30 minutes after that. Supplying any image makes this an edit rather than a generation, at exactly the same price. Up to 5 images, and the extras are free. Send one of image\_handles, image\_urls and image\_base64, never two. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"image.grok","input":{"prompt":"A vintage espresso machine on a marble counter, morning window light, film grain."}}' ``` ## Response The full response. Values are illustrative. ```json theme={null} [ "https://example.com/goro/generated-image-1.png" ] ``` # Nano Banana 2 image Source: https://docs.usegoro.ai/tools/image/image-nano_banana Generate an original image from a text prompt, or EDIT images you supply, using Google Nano Banana 2 at 2K resolution. Send image_handles, image_urls or image_b Generate an original image from a text prompt, or EDIT images you supply, using Google Nano Banana 2 at 2K resolution. Send image\_handles, image\_urls or image\_base64 to edit, restyle or combine up to 14 reference images; send none and it generates from the prompt alone. Editing costs exactly what generating costs and the extra references are free. Returns the URL of the generated image. Those URLs are temporary: Goro holds a run's results for 24 hours and nothing guarantees the file outlives that, so collect what you need within 24 hours. Use when you need an image made to order rather than found, OR when you have an image and want it changed: a hero image, an ad creative, a product mockup, an illustration, a background swapped, a product dropped into a scene, several references merged into one composition. Strongest of the cheaper image models at prompt following and at legible text inside the image. For the hardest edits and the best text, use image.nano\_banana\_pro. One image out per call. ## Price **$0.12 per call at `2K`.** A flat price, and the only thing that changes it is `resolution`: `1K` costs **$0.08**, `2K` costs **$0.12**, `4K` costs **$0.18**. Every one of those is exact and known before the call, so the hold and the charge are the same number and there is no ceiling to reconcile. Omit `resolution` and you are billed the `2K` price. A call that fails, times out or is stopped releases its hold in full and costs nothing. ## Input | Field | Type | Required | Notes | | --------------- | ----------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `prompt` | `string` | yes | What to draw. Describe subject, composition, style, lighting and mood; the more specific, the closer the result. | | `image_urls` | `array` | | Optional. Public http or https links to the images to edit, combine or use as reference (JPEG, PNG or WEBP, up to 10 MB each, up to 14 of them). Use this when the images are ALREADY published somewhere; the output URL of any image endpoint works directly, which is what makes generate-then-edit, and merging two generations, a two-call sequence with nothing to upload in between. Nothing is copied or re-hosted, we pass the links on. For files that are not already public, use image\_handles instead and they never have to be published anywhere. Send one of the three, never two. | | `resolution` | `1K`, `2K`, `4K` | | Output resolution, and the only thing that changes the price: $0.08 at 1K, $0.12 at 2K, \$0.18 at 4K. Defaults to 2K. Ask for 1K on drafts and volume work, 4K when the image is final. Defaults to `"2K"`. | | `aspect_ratio` | `1:1`, `2:3`, `3:2`, `3:4`, `4:3`, `4:5`, `5:4`, `9:16`, `16:9`, `21:9` | | Shape of the output image. Defaults to `"1:1"`. | | `image_base64` | `string` | | Optional, and only for ONE small image (under 3 MB) when you cannot run a shell command AND cannot ask the user to open a link. The image inline, as base64 of the file's own bytes; a data:image/png;base64, prefix is accepted and its declared type ignored. Only one image can arrive this way, because every inline image shares one request body. Understand the cost before choosing it: on an MCP call the request IS the tool arguments, which pass through the model's context window. A 3 MB photograph is 4 MB of base64, on the order of a million tokens, and in practice it is truncated and the run fails looking like file corruption. Do not downscale an image to fit here: use image\_handles, which takes up to 10 MB per file and keeps them out of the conversation entirely. JPEG, PNG or WEBP, checked against the file's own leading bytes rather than against any type you declare. Send one of the three, never two. | | `image_handles` | `array` | | Optional, and the RIGHT WAY to send local or private images to edit. Upload each file first with POST /v1/uploads (or the create\_upload MCP tool, which also hands back a link a PERSON can open to pick the file on their own device, for when you have no shell) and put the short handles it returns here, in order. The files go straight from you to a private bucket: they never pass through a tool call, so they never pass through a model's context window, and up to 10 MB each is fine. Nothing is published. A handle is usable for 10 minutes after its upload lands and the file is deleted 30 minutes after that. Supplying any image makes this an edit rather than a generation, at exactly the same price. Up to 14 images, and the extras are free. Send one of image\_handles, image\_urls and image\_base64, never two. | | `output_format` | `png`, `jpg` | | File format of the generated image. Defaults to `"png"`. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"image.nano_banana","input":{"prompt":"example"}}' ``` ## Response The full response. Values are illustrative. ```json theme={null} [ "https://example.com/goro/generated-image-1.png" ] ``` # Nano Banana Pro image Source: https://docs.usegoro.ai/tools/image/image-nano_banana_pro Generate an original image from a text prompt, or EDIT images you supply, using Google Nano Banana Pro at 2K resolution. Send image_handles, image_urls or image Generate an original image from a text prompt, or EDIT images you supply, using Google Nano Banana Pro at 2K resolution. Send image\_handles, image\_urls or image\_base64 to edit, restyle or combine up to 8 reference images; send none and it generates from the prompt alone. Editing costs exactly what generating costs and the extra references are free. The most capable image model in the catalog, and priced accordingly. Returns the URL of the generated image. Those URLs are temporary: Goro holds a run's results for 24 hours and nothing guarantees the file outlives that, so collect what you need within 24 hours. Use when the image itself is the deliverable and it has to hold up: a campaign asset, a page hero, a composite that has to look real, dense or fiddly text rendered inside the image, an edit that has to preserve a face or a product exactly. Half again the price of image.nano\_banana, which is the one to reach for on volume work and drafts. One image out per call. ## Price **$0.18 per call at `2K`.** A flat price, and the only thing that changes it is `resolution`: `2K` costs **$0.18**, `4K` costs **\$0.24**. Every one of those is exact and known before the call, so the hold and the charge are the same number and there is no ceiling to reconcile. Omit `resolution` and you are billed the `2K` price. A call that fails, times out or is stopped releases its hold in full and costs nothing. ## Input | Field | Type | Required | Notes | | --------------- | ----------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `prompt` | `string` | yes | What to make. Generating: describe subject, composition, style, lighting and mood; the more specific, the closer the result. Editing: describe the CHANGE rather than the whole picture ("replace the background with a sunlit kitchen, keep the product exactly as it is"). With several references you can refer to them in order, e.g. "put the product from the first image onto the table in the second". | | `image_urls` | `array` | | Optional. Public http or https links to the images to edit, combine or use as reference (JPEG, PNG or WEBP, up to 10 MB each, up to 8 of them). Use this when the images are ALREADY published somewhere; the output URL of any image endpoint works directly, which is what makes generate-then-edit, and merging two generations, a two-call sequence with nothing to upload in between. Nothing is copied or re-hosted, we pass the links on. For files that are not already public, use image\_handles instead and they never have to be published anywhere. Send one of the three, never two. | | `resolution` | `2K`, `4K` | | Output resolution, and the only thing that changes the price: $0.18 at 2K, $0.24 at 4K. Defaults to 2K. 1K is not offered because this model costs the same at 1K as at 2K, so it would be a smaller image for the same money. Defaults to `"2K"`. | | `aspect_ratio` | `1:1`, `2:3`, `3:2`, `3:4`, `4:3`, `4:5`, `5:4`, `9:16`, `16:9`, `21:9` | | Shape of the generated image. Does not change the price. Defaults to `"1:1"`. | | `image_base64` | `string` | | Optional, and only for ONE small image (under 3 MB) when you cannot run a shell command AND cannot ask the user to open a link. The image inline, as base64 of the file's own bytes; a data:image/png;base64, prefix is accepted and its declared type ignored. Only one image can arrive this way, because every inline image shares one request body. Understand the cost before choosing it: on an MCP call the request IS the tool arguments, which pass through the model's context window. A 3 MB photograph is 4 MB of base64, on the order of a million tokens, and in practice it is truncated and the run fails looking like file corruption. Do not downscale an image to fit here: use image\_handles, which takes up to 10 MB per file and keeps them out of the conversation entirely. JPEG, PNG or WEBP, checked against the file's own leading bytes rather than against any type you declare. Send one of the three, never two. | | `image_handles` | `array` | | Optional, and the RIGHT WAY to send local or private images to edit. Upload each file first with POST /v1/uploads (or the create\_upload MCP tool, which also hands back a link a PERSON can open to pick the file on their own device, for when you have no shell) and put the short handles it returns here, in order. The files go straight from you to a private bucket: they never pass through a tool call, so they never pass through a model's context window, and up to 10 MB each is fine. Nothing is published. A handle is usable for 10 minutes after its upload lands and the file is deleted 30 minutes after that. Supplying any image makes this an edit rather than a generation, at exactly the same price. Up to 8 images, and the extras are free. Send one of image\_handles, image\_urls and image\_base64, never two. | | `output_format` | `png`, `jpg` | | File type of the returned image. png keeps hard edges and text crisp; jpg is smaller. Does not change the price. Defaults to `"png"`. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"image.nano_banana_pro","input":{"prompt":"example"}}' ``` ## Response The full response. Values are illustrative. ```json theme={null} [ "https://example.com/goro/generated-image-1.png" ] ``` # Qwen Image 3.0 Pro Source: https://docs.usegoro.ai/tools/image/image-qwen Generate an original image from a text prompt using Alibaba Qwen Image 3.0 Pro, at 1K or 2K. Text to image only. Returns the URL of the generated image. That UR Generate an original image from a text prompt using Alibaba Qwen Image 3.0 Pro, at 1K or 2K. Text to image only. Returns the URL of the generated image. That URL is temporary: Goro holds a run's results for 24 hours and nothing guarantees the file outlives that, so collect what you need within 24 hours. Use for structured layouts, posters and infographics with a lot of legible text, and for realistic scenes described in Chinese or English. A negative prompt is available for the things you do not want in the frame. Text to image only: this model bills every reference image, so editing is not offered here. ## Price **$0.12 per call at `2K`.** A flat price, and the only thing that changes it is `resolution`: `1K` costs **$0.0640**, `2K` costs **\$0.12**. Every one of those is exact and known before the call, so the hold and the charge are the same number and there is no ceiling to reconcile. Omit `resolution` and you are billed the `2K` price. A call that fails, times out or is stopped releases its hold in full and costs nothing. ## Input | Field | Type | Required | Notes | | ----------------- | --------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- | | `prompt` | `string` | yes | What to draw. Describe subject, composition, style, lighting and mood; Chinese and English both work. | | `image_size` | `1:1`, `3:2`, `2:3`, `4:3`, `3:4`, `16:9`, `9:16`, `21:9` | | Shape of the output image (this model calls its aspect ratio image\_size). Defaults to `"1:1"`. | | `resolution` | `1K`, `2K` | | Output resolution, and the only thing that changes the price: $0.064 at 1K, $0.12 at 2K. Defaults to 2K. Defaults to `"2K"`. | | `output_format` | `png`, `jpeg` | | File format of the generated image. Defaults to `"png"`. | | `negative_prompt` | `string` | | Optional. What must not appear in the image. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"image.qwen","input":{"prompt":"A poster for a jazz night, art deco lettering reading 'Blue Hour', navy and gold."}}' ``` ## Response The full response. Values are illustrative. ```json theme={null} [ "https://example.com/goro/generated-image-1.png" ] ``` # Seedream 5 Lite image Source: https://docs.usegoro.ai/tools/image/image-seedream Generate one original image from a text prompt using Seedream 5 Lite, at its 2K quality tier. Returns the URL of the generated image. Those URLs are temporary: Generate one original image from a text prompt using Seedream 5 Lite, at its 2K quality tier. Returns the URL of the generated image. Those URLs are temporary: Goro holds a run's results for 24 hours and nothing guarantees the file outlives that, so collect what you need within 24 hours. Use for photorealistic commercial imagery: product shots, interiors, food, lifestyle scenes. Handles long, detailed briefs well and takes the widest range of aspect ratios of the four image models. ## Price **\$0.0550 per call.** A flat price. Every call that succeeds costs exactly this, whatever it returns, and you know the number before you make the call. There is no ceiling to read and no usage to reconcile afterwards. A call that fails, times out or is stopped releases its hold in full and costs nothing. ## Input | Field | Type | Required | Notes | | --------------- | --------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `prompt` | `string` | yes | What to draw. Describe subject, composition, style, lighting and mood; the more specific, the closer the result. | | `quality` | `basic` | | Fixed at basic (2K output). Higher tiers are priced separately upstream, and this endpoint quotes one exact price, so they get their own endpoints. Defaults to `"basic"`. | | `aspect_ratio` | `1:1`, `2:3`, `3:2`, `3:4`, `4:3`, `9:16`, `16:9`, `21:9` | | Shape of the output image. Defaults to `"1:1"`. | | `output_format` | `png`, `jpeg` | | File format of the generated image. Defaults to `"png"`. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"image.seedream","input":{"prompt":"example"}}' ``` ## Response The full response. Values are illustrative. ```json theme={null} [ "https://example.com/goro/generated-image-1.png" ] ``` # Seedream 5.0 Pro image Source: https://docs.usegoro.ai/tools/image/image-seedream_pro Generate an original image from a text prompt using ByteDance Seedream 5.0 Pro, or EDIT one image you supply. Send image_handles, image_urls or image_base64 wit Generate an original image from a text prompt using ByteDance Seedream 5.0 Pro, or EDIT one image you supply. Send image\_handles, image\_urls or image\_base64 with a single reference to restyle or retouch it; send none and it generates from the prompt alone. Editing costs exactly what generating costs. Returns the URL of the generated image. That URL is temporary: Goro holds a run's results for 24 hours and nothing guarantees the file outlives that, so collect what you need within 24 hours. Use for controlled, realistic generation with strong portraits, dense information graphics and multilingual text in the image, one step up from image.seedream (the Lite model). quality basic is a 1K image, high is 2K. One reference image only: this model bills extra input images, so a second one is refused rather than quoted wrong. ## Price **$0.07 per call at `basic`.** A flat price, and the only thing that changes it is `quality`: `basic` costs **$0.07**, `high` costs **\$0.14**. Every one of those is exact and known before the call, so the hold and the charge are the same number and there is no ceiling to reconcile. Omit `quality` and you are billed the `basic` price. A call that fails, times out or is stopped releases its hold in full and costs nothing. ## Input | Field | Type | Required | Notes | | --------------- | --------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `prompt` | `string` | yes | What to draw, or what to change. Describe subject, composition, style, lighting and mood; the more specific, the closer the result. | | `quality` | `basic`, `high` | | Output tier, and the only thing that changes the price: basic is a 1K image at $0.07, high is a 2K image at $0.14. Defaults to `"basic"`. | | `image_urls` | `array` | | Optional. Public http or https links to the images to edit, combine or use as reference (JPEG, PNG or WEBP, up to 10 MB each, up to 1 of them). Use this when the images are ALREADY published somewhere; the output URL of any image endpoint works directly, which is what makes generate-then-edit a two-call sequence with nothing to upload in between. Nothing is copied or re-hosted, we pass the links on. For files that are not already public, use image\_handles instead and they never have to be published anywhere. Send one of the three, never two. | | `aspect_ratio` | `1:1`, `4:3`, `3:4`, `16:9`, `9:16`, `2:3`, `3:2`, `21:9` | | Shape of the output image. Defaults to `"1:1"`. | | `image_base64` | `string` | | Optional, and only for ONE small image (under 3 MB) when you cannot run a shell command AND cannot ask the user to open a link. The image inline, as base64 of the file's own bytes; a data:image/png;base64, prefix is accepted and its declared type ignored. Only one image can arrive this way, because every inline image shares one request body. Understand the cost before choosing it: on an MCP call the request IS the tool arguments, which pass through the model's context window. A 3 MB photograph is 4 MB of base64, on the order of a million tokens, and in practice it is truncated and the run fails looking like file corruption. Do not downscale an image to fit here: use image\_handles, which takes up to 10 MB per file and keeps them out of the conversation entirely. JPEG, PNG or WEBP, checked against the file's own leading bytes rather than against any type you declare. Send one of the three, never two. | | `image_handles` | `array` | | Optional, and the RIGHT WAY to send local or private images to edit. Upload each file first with POST /v1/uploads (or the create\_upload MCP tool, which also hands back a link a PERSON can open to pick the file on their own device, for when you have no shell) and put the short handles it returns here, in order. The files go straight from you to a private bucket: they never pass through a tool call, so they never pass through a model's context window, and up to 10 MB each is fine. Nothing is published. A handle is usable for 10 minutes after its upload lands and the file is deleted 30 minutes after that. Supplying any image makes this an edit rather than a generation, at exactly the same price. Up to 1 image. Send one of image\_handles, image\_urls and image\_base64, never two. | | `output_format` | `png`, `jpeg` | | File format of the generated image. Defaults to `"png"`. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"image.seedream_pro","input":{"prompt":"A street market in Lisbon at dusk, tiled facades, warm lanterns, documentary photograph."}}' ``` ## Response The full response. Values are illustrative. ```json theme={null} [ "https://example.com/goro/generated-image-1.png" ] ``` # Z-Image image Source: https://docs.usegoro.ai/tools/image/image-z_image Generate one original image from a text prompt using Z-Image, the cheapest and fastest model in the catalog. Returns the URL of the generated image. Those URLs Generate one original image from a text prompt using Z-Image, the cheapest and fastest model in the catalog. Returns the URL of the generated image. Those URLs are temporary: Goro holds a run's results for 24 hours and nothing guarantees the file outlives that, so collect what you need within 24 hours. Use when you need an image cheaply or at volume: drafts, thumbnails, placeholder art, or trying ten prompt variations before committing to an expensive model. Photorealistic results, short prompts only. ## Price **\$0.0080 per call.** A flat price. Every call that succeeds costs exactly this, whatever it returns, and you know the number before you make the call. There is no ceiling to read and no usage to reconcile afterwards. A call that fails, times out or is stopped releases its hold in full and costs nothing. ## Input | Field | Type | Required | Notes | | -------------- | ----------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------- | | `prompt` | `string` | yes | What to draw. Describe subject, composition, style, lighting and mood; the more specific, the closer the result. | | `aspect_ratio` | `1:1`, `3:4`, `4:3`, `9:16`, `16:9` | | Shape of the output image. Defaults to `"1:1"`. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"image.z_image","input":{"prompt":"example"}}' ``` ## Response The full response. Values are illustrative. ```json theme={null} [ "https://example.com/goro/generated-image-1.png" ] ``` # Google Maps places Source: https://docs.usegoro.ai/tools/maps/maps-places Search Google Maps for places matching a query and get back name, address, rating, review count, phone, website, and coordinates. Search Google Maps for places matching a query and get back name, address, rating, review count, phone, website, and coordinates. Use when you need local businesses or points of interest, lead lists for a city or area, or place details like ratings and contact info. ## Price **\$0.0090 per place.** That is the rate you pay for each place the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer places costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | --------------- | ----------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `city` | `string` | | City to search in, e.g. Pittsburgh. Do not put the state or country here, they have their own fields. | | `state` | `string` | | State or region. Mostly useful for US addresses. | | `county` | `string` | | County, regional district or departement, depending on the country. | | `website` | `allPlaces`, `withWebsite`, `withoutWebsite` | | Keep only places that have a website, or only those that do not. COSTS EXTRA: \$0.001 per place, as one search filter. | | `language` | `string` | | Defaults to `"en"`. | | `placeIds` | `array` | | Google place IDs, in the form ChIJreV9aqYWdkgROM\_boL6YbwA. One place, and one billed place, per ID. | | `maxImages` | `integer` | | Additional images per place. 0 returns none, 99999 returns all. COSTS EXTRA: $0.0005 per image, plus $0.002 per place for the detail page images force open. | | `startUrls` | `array` | | Google Maps URLs to scrape directly instead of searching. Place, search-results, cid and goo.gl/maps links all work. Each entry is an object with a url. Max 300 places per search URL. | | `maxReviews` | `integer` | | Reviews per place. 0 returns none, 99999 returns all. PERSONAL DATA: each review carries its author unless you set scrapeReviewsPersonalData false. COSTS EXTRA: $0.0005 per review, plus $0.002 per place for the detail page reviews force open. A large number here cannot push the bill past this call's ceiling, it just means fewer places come back. | | `postalCode` | `string` | | One postal code to search in. Combine it with countryCode, never with city. | | `countryCode` | `string` | | Two-letter country to search in, e.g. us, gb, de. These five location fields are more reliable than packing everything into locationQuery. | | `reviewsSort` | `newest`, `mostRelevant`, `highestRanking`, `lowestRanking` | | Order reviews are collected in. The actor uses newest when you leave it out. | | `maxQuestions` | `integer` | | Questions and answers per place. 0 returns only the first pair, 999 returns all. PERSONAL DATA: the answers carry the asker's and the answerer's names. COSTS EXTRA: \$0.002 per place. | | `locationQuery` | `string` | | Where to search, e.g. "Berlin, Germany" | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"maps.places","input":{"searchStringsArray":["example"],"maxCrawledPlacesPerSearch":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "lat": 44.4322, "lng": 26.0993, "url": "https://maps.google.com/?cid=123", "name": "Sam Okafor", "phone": "+1 555 0100", "rating": 4.6, "address": "Strada Lipscani 9, Bucharest", "website": "https://origocoffee.ro", "reviews_count": 2731 } ``` # Google Maps reviews Source: https://docs.usegoro.ai/tools/maps/maps-reviews Reviews for a Google Maps place, with star rating, review text, publish date, owner response, review images and visit context, alongside the place's own address Reviews for a Google Maps place, with star rating, review text, publish date, owner response, review images and visit context, alongside the place's own address, category, total score and review count. Use to read what customers actually say about a business or landmark, track rating trends over time, or mine complaints and praise. Identify places by placeIds or Google Maps place URLs. Set personalData to false when you do not need reviewer identities. maxReviews is per place. ## Price **\$0.001350 per review.** That is the rate you pay for each review the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer reviews costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | --------------------- | ----------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `language` | `string` | | Language the review details are returned in, e.g. en, fr, de, es, it, ro Defaults to `"en"`. | | `placeIds` | `array` | | Google place IDs, e.g. ChIJLU7jZClu5kcR4PcOOO6p3I0. More precise than URLs | | `startUrls` | `array` | | Google Maps place, search or reviews URLs. Must contain /maps/place, /maps/search or /maps/reviews | | `maxReviews` | `integer` | | Maximum reviews PER PLACE. Billed per review. The actor's own default is unlimited, this endpoint defaults to 100 so one call cannot run away Defaults to `100`. | | `reviewsSort` | `newest`, `mostRelevant`, `highestRanking`, `lowestRanking` | | Defaults to `"newest"`. | | `personalData` | `boolean` | | Include reviewer identity (id, name, profile URL, photo) and the review URL. Set to false to get reviews without personal data Defaults to `false`. | | `reviewsOrigin` | `all`, `google` | | all includes Tripadvisor and other sources, google restricts to Google reviews Defaults to `"all"`. | | `reviewsStartDate` | `string` | | Only reviews newer than this, absolute (2026-05-03) or relative ("3 months") | | `reviewsFilterString` | `string` | | Only reviews containing this keyword | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"maps.reviews","input":{"maxReviews":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "url": "https://www.google.com/maps/search/?api=1&query=Eiffel%20Tower&query_place_id=ChIJLU7jZClu5kcR4PcOOO6p3I0", "city": "Paris", "name": "Priya Raman", "text": "It's a tower made of steel. The shaded areas and trees nearby help a lot in summer.", "stars": 3, "title": "Eiffel Tower", "address": "Av. Gustave Eiffel, 75007 Paris, France", "placeId": "ChIJLU7jZClu5kcR4PcOOO6p3I0", "location": { "lat": 48.8583701, "lng": 2.2944813 }, "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT2t0b05FTjJPV2w0Yg", "publishAt": "Edited 2 weeks ago", "reviewUrl": "https://www.google.com/maps/reviews/data=!4m8!14m7!1m6!2m5!1sCi9DQUlRQUNvZA", "visitedIn": "Visited in July", "likesCount": 0, "reviewerId": "104712345678901234567", "totalScore": 4.7, "countryCode": "FR", "reviewerUrl": "https://www.google.com/maps/contrib/104712345678901234567", "categoryName": "Historical landmark", "isLocalGuide": true, "reviewOrigin": "Google", "reviewsCount": 492458, "reviewContext": { "Wait time": "Up to 10 min", "Visited on": "Weekend" }, "textTranslated": null, "publishedAtDate": "2026-07-15T20:02:44.481Z", "reviewImageUrls": [ "https://lh3.googleusercontent.com/grass-cs/example" ], "originalLanguage": "en", "responseFromOwnerDate": null, "responseFromOwnerText": null, "reviewerNumberOfReviews": 85 } ``` # Tool catalog Source: https://docs.usegoro.ai/tools/overview Everything your agent can reach through one connection. Goro exposes 80 tools: search, social, maps, commerce and people data, plus image generation, video generation and voice. You do not need to memorise them: `discover` ranks the catalog against a plain-English question and hands back the best matches with their prices. ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/discover \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "recent posts from a tiktok creator"}' ``` The pages in this section document each tool's input and the shape of what it returns. Every one of them is generated from the live catalog, so the fields listed are the fields the tool actually accepts today. ## How to read a tool page **Input** lists the fields the tool takes. Most of the catalog is a passthrough: your input reaches the underlying tool unchanged, so anything it accepts works even when it is not in the table. The image and voice tools are the exception, and the note under their table says so. Fields that cost extra say so too. **Price** is what you pay, all in. Three shapes exist and they do not mean the same thing, so read the words next to the number. [Pricing](/pricing) covers all three. **Response** shows one row, or the whole thing where it is short enough to print. The values are illustrative, not real records. ## Three providers behind one balance The data tools run on Apify. Image and video generation run on kie.ai. Voice runs on Fish Audio. You never hold an account or a key with any of them, and every call draws from the same balance. The difference you can see is in the price. Apify bills us for what a scrape actually consumed, so those tools quote a rate per row and settle on the rows they really produced. kie.ai charges a fixed amount per image and a rate per second of video, and Fish bills a published rate against a measurable input, so those tools quote an exact price you can work out before you call. ## Collecting a result Rows are held until you collect them and never longer than 24 hours. Media is the case worth planning for: a voice call returns a link to audio that stops working on the same 24 hour clock, and an image call returns links hosted by kie.ai. Download what you want to keep, in the same task that made the call. [The API overview](/api/overview#run-results-are-held-only-until-you-collect-them) has the full contract. ## A note on personal data Several tools in this catalog return information about identifiable people. You are responsible for having a lawful basis to collect it, and for complying with the terms of the platform the data came from. Goro records what a call cost and how many rows it returned permanently. The rows themselves are held only until you collect them, and never longer than 24 hours. # Contact and email finder Source: https://docs.usegoro.ai/tools/people/email-finder Contact details published on a website: email addresses, phone numbers, and the pages each was found on, for every domain or page URL you send. Addresses are co Contact details published on a website: email addresses, phone numbers, and the pages each was found on, for every domain or page URL you send. Addresses are collected as the site publishes them and are not tested for deliverability. Use when you have company domains, creator sites or page URLs and need someone to contact: building an outreach list, enriching leads that carry only a website, or finding the humans behind a business. One result per entry, and entries where nothing is found return no row and cost nothing. Check deliverability separately before a large send, since these addresses are collected as published, not validated. ## Price **\$0.06 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | --------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `domains` | `array` | yes | Domains or page URLs to scan, for example "example.com" or "[https://example.com/about](https://example.com/about)". At most one result, and at most one billable item, per entry: an entry with no contacts found returns nothing and is not billed. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"email.finder","input":{"domains":["example"]}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "links": [ "https://example.com/contact", "https://example.com/about/press", "https://www.linkedin.com/company/example" ], "domain": "example.com", "emails": [ "hello@example.com", "press@example.com", "dana.whitfield@example.com" ], "inputUrl": "https://example.com/about", "phoneNumbers": [ "+15550117342", "+442071234567" ] } ``` # LinkedIn company employees Source: https://docs.usegoro.ai/tools/people/linkedin-company_employees Lists the people who work at one or more LinkedIn companies, with filters for job title, seniority, function, location, tenure, and years of experience. Email l Lists the people who work at one or more LinkedIn companies, with filters for job title, seniority, function, location, tenure, and years of experience. Email lookup is available as a paid mode. Use to map an organisation: find decision makers at a target account, size a department, or build a contact list for a named company. Prefer this over keyword search when you know the employer. ## Price **\$0.0240 per profile.** That is the rate you pay for each profile the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer profiles costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ------------------ | --------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `maxItems` | `integer` | | Maximum employees to return. This is the field the price quote is sized from. Defaults to `25`. | | `companies` | `array` | yes | Full LinkedIn company URLs, for example [https://www.linkedin.com/company/google](https://www.linkedin.com/company/google). | | `jobTitles` | `array` | | Current job title filter, for example "Head of Sales". | | `locations` | `array` | | LinkedIn location filter. Prefer the full country or city name. | | `startPage` | `integer` | | Search page to start from. Each page holds 25 profiles. | | `takePages` | `integer` | | Number of search pages to scrape, 25 profiles each. Setting this above maxItems/25 raises the real cost without raising the quote, so prefer maxItems. | | `functionIds` | `array` | | Function filter, for example "8" for Engineering, "25" for Sales. | | `industryIds` | `array` | | LinkedIn industry ID filter, for example "4" for Software Development. | | `searchQuery` | `string` | | Fuzzy search within the company's employees. | | `pastJobTitles` | `array` | | Past job title filter. | | `excludeSchools` | `array` | | Drop profiles who attended these schools, for example Stanford University. | | `companyBatchMode` | `all_at_once`, `one_by_one` | | How multiple companies are queried. one\_by\_one runs a separate query per company and therefore pays the \$0.02 actor-start fee once PER COMPANY, so it is the single most expensive flag on this endpoint. Defaults to `"all_at_once"`. | | `companyHeadcount` | `array` | | Current employer headcount band, A self-employed through I 10001+. | | `excludeLocations` | `array` | | Locations to exclude. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"linkedin.company_employees","input":{"companies":["example"],"maxItems":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "ACoAAsampleProfileId00000000000000000", "photo": "https://example.com/media/sample.jpg", "emails": [], "premium": false, "headline": "Chief Executive Officer at Example Corp", "lastName": "Sam Okafor", "location": { "countryCode": "US", "linkedinText": "San Francisco Bay Area" }, "education": [], "firstName": "Priya Raman", "experience": [], "linkedinUrl": "https://linkedin.com/example-account", "followerCount": 8146, "currentPosition": [ { "position": "Founder and CEO", "companyName": "Apify", "companyLinkedinUrl": "https://linkedin.com/example-account/apify/" } ], "connectionsCount": 4502, "publicIdentifier": "jancurn" } ``` # LinkedIn job search Source: https://docs.usegoro.ai/tools/people/linkedin-jobs Searches LinkedIn job postings by title and location, with filters for company, workplace type, employment type, seniority, salary band, and recency. Returns th Searches LinkedIn job postings by title and location, with filters for company, workplace type, employment type, seniority, salary band, and recency. Returns the full posting, the hiring company, applicant counts, and the apply link. Use to track hiring signals: which companies are staffing up a function, what a role pays in a market, or which accounts just opened a req you can sell into. Also useful as an intent signal before outreach. ## Price **\$0.0030 per job.** That is the rate you pay for each job the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer jobs costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ----------------- | ---------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `page` | `integer` | | Search page to start from. Each page holds 25 jobs. | | `geoIds` | `array` | | LinkedIn geo ID filter, for example "103644278". More precise than locations. | | `salary` | `array` | | Minimum salary band filter. | | `sortBy` | `date`, `relevance` | | Result ordering. Defaults to `"date"`. | | `company` | `array` | | Filter by employer. Full LinkedIn company URLs or company names. | | `maxItems` | `integer` | | Maximum jobs per title and location pair. This is the field the price quote is sized from. Defaults to `10`. | | `easyApply` | `boolean` | | Only postings with LinkedIn Easy Apply. | | `jobTitles` | `array` | yes | Search queries, as typed into LinkedIn job search. Boolean operators are supported. maxItems applies to EACH title and location pair, so two titles across two locations can cost four times the quote. | | `locations` | `array` | | LinkedIn location filter, for example "New York". Prefer the full country or city name. | | `industryIds` | `array` | | LinkedIn industry ID filter, for example "4" for Software Development. | | `postedLimit` | `1h`, `24h`, `week`, `month` | | Only postings newer than this. | | `workplaceType` | `array` | | Workplace type filter. | | `employmentType` | `array` | | Employment type filter. | | `experienceLevel` | `array` | | Seniority filter. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"linkedin.jobs","input":{"jobTitles":["example"],"maxItems":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "4308191234", "new": true, "query": { "search": "software engineer", "sortBy": "date", "location": "New York" }, "title": "Software Engineer", "views": 412, "salary": { "max": 210000, "min": 150000, "currency": "USD", "payPeriod": "YEARLY", "providedByEmployer": true }, "company": { "id": "1441", "name": "Priya Raman", "website": "https://example.com", "linkedinUrl": "https://linkedin.com/example-account/example", "employeeCount": 12000, "followerCount": 980000 }, "expireAt": "2026-08-27T09:12:00.000Z", "jobState": "LISTED", "location": { "geoId": "105080838", "countryCode": "US", "linkedinText": "New York, NY" }, "applicants": 37, "hiringTeam": [ { "name": "Jordan Blake", "position": "Engineering Manager", "linkedinUrl": "https://linkedin.com/example-account" } ], "industries": [ "Software Development" ], "postedDate": "2026-07-28T09:12:00.000Z", "applyMethod": { "type": "OffsiteApply", "companyApplyUrl": "https://example.com/careers/4308191234" }, "linkedinUrl": "https://linkedin.com/example-account/view/4308191234", "jobFunctions": [ "Engineering" ], "workplaceType": "hybrid", "employmentType": "FULL_TIME", "descriptionText": "We are hiring a software engineer to work on our platform team.", "experienceLevel": "mid-senior", "workRemoteAllowed": false } ``` # LinkedIn profile Source: https://docs.usegoro.ai/tools/people/linkedin-profile Full LinkedIn profile records for a list of profile URLs: headline, current role, employer details, full work history, education, skills, certifications, langua Full LinkedIn profile records for a list of profile URLs: headline, current role, employer details, full work history, education, skills, certifications, languages, and a contact email or phone when the actor can resolve one. Use when you already have LinkedIn profile URLs and need the complete record behind each one, for lead enrichment, candidate research, or confirming someone's current title and employer. ## Price **\$0.03 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `profileUrls` | `array` | yes | LinkedIn profile URLs to enrich, for example [https://www.linkedin.com/in/example-profile](https://www.linkedin.com/in/example-profile). One result, and one billable item, per URL. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"linkedin.profile","input":{"profileUrls":["example"]}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "about": "Founder of Example Ventures. Writes about climate technology.", "email": null, "skills": [ { "title": "Philanthropy" } ], "fullName": "Jordan Blake", "headline": "Founder at Example Ventures", "jobTitle": "Co-chair", "lastName": "Mia Fontaine", "firstName": "Tomas Novak", "isPremium": true, "educations": [ { "title": "Harvard University", "subtitle": "Drop out" } ], "isVerified": true, "companyName": "Bill & Melinda Gates Foundation", "companySize": "1001-5000", "experiences": [ { "title": "Co-chair", "companyName": "Bill & Melinda Gates Foundation", "jobStartedOn": "2000", "jobStillWorking": true } ], "linkedinUrl": "https://linkedin.com/example-account", "mobileNumber": null, "companyWebsite": "gatesfoundation.org", "companyIndustry": "Philanthropic Fundraising Services", "publicIdentifier": "example-profile", "addressWithCountry": "Seattle, Washington, United States", "totalExperienceYears": 48 } ``` # LinkedIn profile search by name Source: https://docs.usegoro.ai/tools/people/linkedin-search_name Find LinkedIn profiles by a person's first and last name, with optional filters for location, current or past company, school, job title and industry. Find LinkedIn profiles by a person's first and last name, with optional filters for location, current or past company, school, job title and industry. Use when you know who you are looking for by name and want their LinkedIn profile. To search by role or skill rather than by name, use linkedin.search\_services instead. ## Price **\$0.0120 per profile.** That is the rate you pay for each profile the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer profiles costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | -------------------- | -------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `schools` | `array` | | School names to filter by, e.g. "Stanford University" | | `lastName` | `string` | yes | Last name of the person to find | | `maxItems` | `integer` | | Maximum profiles to return. This is the field the price is sized on. Defaults to `10`. | | `maxPages` | `integer` | | Search pages to scrape per query. Each page holds 25 profiles, so raising this raises the bill. Defaults to `1`. | | `firstName` | `string` | yes | First name of the person to find | | `locations` | `array` | | LinkedIn location filters, e.g. "United Kingdom". LinkedIn misreads abbreviations, so spell the country out. | | `industryIds` | `array` | | LinkedIn industry ids to filter by | | `strictSearch` | `boolean` | | Return only profiles matching both first and last name exactly. Turning this off widens the search and can bill many more profiles. Defaults to `true`. | | `pastCompanies` | `array` | | Full LinkedIn company URLs to filter by past employer | | `currentCompanies` | `array` | | Full LinkedIn company URLs to filter by current employer | | `currentJobTitles` | `array` | | Exact current job titles to filter by | | `profileScraperMode` | `Short`, `Full`, `Full + email search` | | Detail level per profile, and what each profile costs. Short bills $0.002, Full bills $0.004, and Full + email search bills \$0.010, so the email mode is 5x the price of Short. Defaults to the cheapest; ask for more only if you need it. Defaults to `"Short"`. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"linkedin.search_name","input":{"firstName":"example","lastName":"example","maxItems":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "ACoAAsampleProfileId00000000000000000", "photo": "https://example.com/media/sample.jpg", "emails": [], "premium": true, "headline": "Head of Engineering at Example Corp", "lastName": "Alex Rivera", "location": { "countryCode": "US", "linkedinText": "Redmond, Washington, United States" }, "education": [], "firstName": "Sam Okafor", "experience": [], "openToWork": false, "linkedinUrl": "https://linkedin.com/example-account", "followerCount": 11200000, "currentPosition": [ { "position": "Chairman and CEO", "companyName": "Microsoft", "companyLinkedinUrl": "https://linkedin.com/example-account/microsoft/" } ], "connectionsCount": 500, "publicIdentifier": "satya-nadella-01272362" } ``` # LinkedIn profile search by keyword Source: https://docs.usegoro.ai/tools/people/linkedin-search_services Searches LinkedIn people by a free-text query such as a service, skill, or role ('fractional CFO', 'machine learning engineer') and returns matching profiles, o Searches LinkedIn people by a free-text query such as a service, skill, or role ("fractional CFO", "machine learning engineer") and returns matching profiles, optionally filtered by location. Email lookup is available as a paid mode. Use to build a list of people by what they do rather than by name, for prospecting, recruiting, or sizing a talent pool in a given market. ## Price **\$0.0120 per profile.** That is the rate you pay for each profile the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer profiles costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | -------------------- | -------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `search` | `string` | yes | Keyword query, for example "Founder" or "machine learning engineer". LinkedIn search operators are supported. | | `maxItems` | `integer` | | Maximum profiles to return. This is the field the price quote is sized from. Defaults to `20`. | | `locations` | `array` | | LinkedIn location filter, for example "United Kingdom". LinkedIn resolves free text loosely, so prefer the full country or city name. | | `startPage` | `integer` | | Search page to start from. Each page holds 25 profiles. Defaults to `1`. | | `takePages` | `integer` | | Number of search pages to scrape, 25 profiles each. Setting this above maxItems/25 raises the real cost without raising the quote, so prefer maxItems. | | `profileScraperMode` | `Short`, `Full`, `Full + email search` | | Detail level per profile, and what each profile costs. Short bills $0.002, Full bills $0.004, and Full + email search bills \$0.010, so the email mode is 2.5x the price of Full. Defaults to `"Short"`. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"linkedin.search_services","input":{"search":"example","maxItems":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "ACoAAsampleProfileId00000000000000000", "name": "Mia Fontaine", "emails": [], "picture": "https://example.com/media/sample.jpg", "premium": false, "headline": "Machine Learning Engineer at Acme AI", "lastName": "Tomas Novak", "location": { "countryCode": "DE", "linkedinText": "Berlin, Germany" }, "education": [], "firstName": "Alex Rivera", "experience": [], "linkedinUrl": "https://linkedin.com/example-account", "followerCount": 3120, "currentPosition": [ { "position": "Machine Learning Engineer", "companyName": "Acme AI" } ], "connectionsCount": 1840, "publicIdentifier": "jane-doe-ml" } ``` # Google News search Source: https://docs.usegoro.ai/tools/search/news-search News articles from Google News for one or more keywords, sections or topic URLs, with headline, publisher, publish time, link and image, in any of 80 region and News articles from Google News for one or more keywords, sections or topic URLs, with headline, publisher, publish time, link and image, in any of 80 region and language pairs. Use for current-events lookups, brand and competitor monitoring, and building a dated news timeline. Narrow timeframe rather than raising maxArticles when you only want fresh coverage. maxArticles applies to EACH keyword or topic. ## Price **\$0.0120 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | --------------------- | ------------------------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `topics` | `array` | | Predefined Google News sections, pulled instead of or alongside keywords | | `keywords` | `array` | | Search keywords. Google News operators work: - excludes, site: restricts to a publisher, OR combines, quotes force an exact phrase. maxArticles applies to EACH keyword | | `timeframe` | `1h`, `1d`, `7d`, `30d`, `1y`, `all` | | Time window for keyword searches. Topic pages return their own curated results Defaults to `"1d"`. | | `topicUrls` | `array` | | Custom Google News topic or section URLs for niche topics | | `decodeUrls` | `boolean` | | Resolve Google News redirect links to the publisher's real URL. Much slower Defaults to `false`. | | `maxArticles` | `integer` | | Maximum articles per keyword or topic. Billed per article Defaults to `20`. | | `extractImages` | `boolean` | | Defaults to `true`. | | `region_language` | `string` | | Region and language pair, e.g. US:en, GB:en, DE:de, FR:fr, ES:es, IT:it, RO:ro, JP:ja Defaults to `"US:en"`. | | `extractDescriptions` | `boolean` | | Fetch each article page for its meta description. Much slower Defaults to `false`. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"news.search","input":{"maxArticles":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "link": "https://news.google.com/rss/articles/CBMiV0FVX3lxTE...", "image": "https://lh3.googleusercontent.com/proxy/example", "title": "Example headline about artificial intelligence", "source": "Reuters", "keyword": "artificial intelligence", "description": "", "publishedAt": "2026-07-30T09:12:00.000Z", "region_language": "US:en" } ``` # Google Trends Source: https://docs.usegoro.ai/tools/search/trends-search Google search demand for a term over time, as an interest index of 0 to 100 with an optional ranked regional breakdown, or the searches spiking in one country r Google search demand for a term over time, as an interest index of 0 to 100 with an optional ranked regional breakdown, or the searches spiking in one country right now with estimated volumes and related terms. One result per call in either mode. Use when the question is about SEARCH DEMAND rather than about posts: is interest in this rising or dying, when did it peak, is it seasonal, which regions want it most, or what is spiking in a country today. This is the only endpoint that returns a time series, and it counts everyone who typed the query into Google rather than the small minority who post about it. For what people are SAYING use twitter.search or reddit.posts, for what is popular ON a platform use tiktok.api, for what publishers wrote use news.search, for what ranks use web.search. Set mode first. Keyword mode takes ONE term per call, so a five-term comparison is five calls, and interest is indexed within the geo and window you asked for, so results from different settings are not comparable in absolute terms. Every call costs the same flat amount whether it returns five rows or five hundred, because the cost is almost entirely a fixed start fee. ## Price **\$0.0030 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `geo` | \`\`, `AF`, `ZA`, `AL`, `DZ`, `DE`, `AD`, `AO`, `AI`, `AG`, `SA`, `AR`, `AM`, `AW`, `AU`, `AT`, `AZ`, `BS`, `BH`, `BD`, `BB`, `BE`, `BZ`, `BJ`, `BM`, `BT`, `BY`, `BO`, `BA`, `BW`, `BR`, `BN`, `BG`, `BF`, `BI`, `KH`, `CM`, `CA`, `CV`, `CL`, `CN`, `CY`, `CO`, `KM`, `CG`, `CD`, `KP`, `KR`, `CR`, `CI`, `HR`, `CU`, `CW`, `DK`, `DJ`, `DM`, `EG`, `AE`, `EC`, `ER`, `ES`, `EE`, `US`, `ET`, `FJ`, `FI`, `FR`, `GA`, `GM`, `GE`, `GH`, `GI`, `GR`, `GL`, `HK`, `HU`, `IN`, `ID`, `IQ`, `IR`, `IE`, `IS`, `IL`, `IT`, `JM`, `JP`, `JO`, `KZ`, `KE`, `KW`, `LA`, `LV`, `LB`, `LY`, `LT`, `LU`, `MO`, `MY`, `MV`, `MT`, `MA`, `MX`, `MC`, `MN`, `ME`, `MZ`, `MM`, `NA`, `NP`, `NI`, `NG`, `NO`, `NZ`, `OM`, `UG`, `UZ`, `PK`, `PA`, `PY`, `NL`, `PE`, `PH`, `PL`, `PT`, `QA`, `RO`, `GB`, `RU`, `RW`, `SN`, `RS`, `SG`, `SK`, `SI`, `SO`, `LK`, `SE`, `CH`, `TW`, `TJ`, `TZ`, `TH`, `TN`, `TR`, `UA`, `UY`, `VE`, `VN`, `YE`, `ZM`, `ZW` | | Country to measure interest in, for mode keyword, as a two-letter code. Leave it out, or send the empty string, for worldwide. The 0 to 100 index is computed WITHIN whatever geo and window you ask for, so a 100 in Romania and a 100 in the United States are not the same amount of searching. | | `mode` | `keyword`, `trending` | yes | Which question you are asking. keyword measures one search term over time. trending lists the terms spiking in one country right now. Required here even though the actor has a default, because that default is trending: a call that meant to analyse a keyword and forgot this field gets billed in full for a country's trending list instead. | | `keyword` | `string` | | The search term to measure, for mode keyword. One term per call, since the actor takes a single string and not a list. Leave it out and the actor falls back to its own example term, bitcoin, and charges you for it. | | `fetchRegionalData` | `boolean` | | Add region\_data to a mode keyword result: every region of the chosen country, ranked and scored 0 to 100. Off unless you set it. Needs geo set, since worldwide has no regions to break down. Costs nothing extra, the call is one billed item either way. | | `predefinedTimeframe` | `now 1-h`, `now 4-h`, `now 1-d`, `now 7-d`, `today 1-m`, `today 3-m`, `today 12-m`, `today 5-y`, `all` | | Window for mode keyword. The actor uses today 12-m when you leave it out. Google picks the granularity to fit the window and reports it back as data\_granularity: minute for now 1-h, hour for the other now windows, day up to today 3-m, week for today 12-m, month for today 5-y and all. | | `trendingSearchesCountry` | `US`, `FR`, `AL`, `DZ`, `AO`, `AR`, `AM`, `AU`, `AT`, `AZ`, `BH`, `BD`, `BY`, `BE`, `BJ`, `BO`, `BA`, `BR`, `BG`, `BF`, `KH`, `CM`, `CA`, `CL`, `CO`, `CR`, `HR`, `CU`, `CY`, `CZ`, `CD`, `DK`, `DO`, `EC`, `EG`, `SV`, `EE`, `ET`, `FI`, `GE`, `DE`, `GH`, `GR`, `GT`, `HT`, `HN`, `HK`, `HU`, `IN`, `ID`, `IR`, `IQ`, `IE`, `IL`, `IT`, `CI`, `JM`, `JP`, `JO`, `KZ`, `KE`, `KW`, `KG`, `LV`, `LB`, `LY`, `LT`, `MY`, `ML`, `MX`, `MD`, `MA`, `MZ`, `MM`, `NP`, `NL`, `NZ`, `NI`, `NG`, `MK`, `NO`, `OM`, `PK`, `PS`, `PA`, `PY`, `PE`, `PH`, `PL`, `PT`, `PR`, `QA`, `RO`, `RU`, `SA`, `SN`, `RS`, `SG`, `SK`, `SI`, `ZA`, `KR`, `ES`, `LK`, `SE`, `CH`, `SY`, `TW`, `TZ`, `TH`, `TT`, `TN`, `TR`, `TM`, `UG`, `UA`, `AE`, `GB`, `UY`, `UZ`, `VE`, `VN`, `YE`, `ZM`, `ZW` | | Country for mode trending, as a two-letter code. The actor uses US when you leave it out. This list is not the same as the geo list above: trending covers 125 countries, keyword interest covers 150 plus worldwide. | | `trendingSearchesMaxItems` | `integer` | | How many trending terms to pack into the single result, for mode trending. The actor returns 50 when you leave it out. This does NOT change the price: the whole list arrives inside one dataset item, so asking for 5 costs exactly what asking for 100 costs. | | `trendingSearchesTimeframe` | `4`, `24`, `48`, `168` | | How far back mode trending looks, in HOURS, sent as a string: 4, 24, 48 or 168 for one week. The actor uses 24 when you leave it out. A shorter window surfaces breaking spikes, a longer one surfaces what has stayed up. | | `trendingSearchesCategories` | `array` | | Filter mode trending to these Google Trends categories, as string ids: 1 autos and vehicles, 2 beauty and fashion, 3 business and finance, 4 entertainment, 5 food and drink, 6 games, 7 health, 8 hobbies and leisure, 9 jobs and education, 10 law and government, 11 other, 13 pets and animals, 14 politics, 15 science, 16 shopping, 17 sports, 18 technology, 19 travel and transportation, 20 climate. There is no 12. Every category when you leave it out. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"trends.search","input":{"mode":"keyword"}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "geo": "US", "keyword": "cold plunge", "language": "en-US", "timeframe": "today 12-m", "trends_url": "https://trends.google.com/trends/explore?geo=US&q=cold+plunge&hl=en-US", "region_data": [ { "rank": 1, "value": 100, "region": "Utah" }, { "rank": 2, "value": 94, "region": "Colorado" }, { "rank": 3, "value": 88, "region": "Idaho" } ], "timeline_data": { "isPartial": { "2025-08-03": false, "2025-08-10": false, "2026-07-26": false, "2026-08-02": true }, "cold plunge": { "2025-08-03": 41, "2025-08-10": 44, "2026-07-26": 88, "2026-08-02": 93 } }, "data_granularity": "week" } ``` # Web search Source: https://docs.usegoro.ai/tools/search/web-search Run a Google web search and get back ranked organic results with title, url, and snippet. Run a Google web search and get back ranked organic results with title, url, and snippet. Use when you need current information from the web, to find pages about a topic, or to answer questions that need fresh sources. Billed per result page, about 10 results each. ## Price **\$0.0075 per page.** That is the rate you pay for each page the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer pages costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | --------------------------------- | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `site` | `string` | | Limit to one site, e.g. example.com | | `queries` | `string` | yes | Search term(s), or Google Search URLs. Newline-separate for multiple. Supports advanced operators like site: and OR. | | `saveHtml` | `boolean` | | Attach each results page's raw HTML to its item as html. Useful when you want to parse the page yourself, and it makes the response very large. | | `afterDate` | `string` | | Only results from after this date. Same formats as beforeDate. Do not combine with quickDateRange. | | `fileTypes` | `array` | | Only results of these file types, the filetype: operator, e.g. pdf, xlsx, csv. Several types are combined with OR. | | `aiOverview` | `object` | | PAID ADD-ON. Capture Google's AI Overview in full for each query. Set scrapeFullAiOverview true inside this object. Billed $0.002 per query where an overview is actually captured, against $0.0025 for the search page itself. | | `aiOverview.scrapeFullAiOverview` | `boolean` | | | | `beforeDate` | `string` | | Only results from before this date. Absolute (2026-05-03, read as UTC) or relative (8 days, 3 months). Do not combine with quickDateRange. | | `wordsInUrl` | `array` | | Words that must appear in the URL, the inurl: operator. | | `countryCode` | `string` | | Two-letter country code, sets the Google domain (e.g. es for google.es) | | `wordsInText` | `array` | | Words that must appear in the page text, the intext: operator. | | `aiModeSearch` | `object` | | PAID ADD-ON. Also answer each query from Google AI Mode, the conversational search surface, with cited sources. Set enableAiMode true inside this object. Billed \$0.005 per answer returned, twice the price of the search page. | | `aiModeSearch.enableAiMode` | `boolean` | | | | `geminiSearch` | `object` | | PAID ADD-ON. Also answer each query from Google Gemini. Set enableGemini true inside this object. Billed \$0.005 per answer returned. | | `geminiSearch.enableGemini` | `boolean` | | | | `includeIcons` | `boolean` | | Include base64 icon image data on organic, paid and suggested results. Large. | | `languageCode` | `string` | | Interface language (hl) | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"web.search","input":{"queries":"example","maxPagesPerQuery":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "url": "https://example.com/ai-agents", "title": "What is an AI agent?", "snippet": "An AI agent is a program that can act autonomously...", "position": 1 } ``` # Facebook Ads Library Source: https://docs.usegoro.ai/tools/social/facebook-ads Ads from the public Facebook Ads Library for a page or a keyword search, with creative body, headline, call to action, landing URL, run dates, platforms and pag Ads from the public Facebook Ads Library for a page or a keyword search, with creative body, headline, call to action, landing URL, run dates, platforms and page metadata. Use for competitor ad research: what a brand is running right now, which hooks and landing pages it uses, and how long each ad has been live. Note the billing model, count is the maximum CHARGED results and the actor bills the full count even when fewer ads exist. ## Price **\$0.002250 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ---------------------------- | ----------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `urls` | `array` | yes | Facebook page URLs, or Ads Library search URLs such as [https://www.facebook.com/ads/library/?q=nike\&country=US](https://www.facebook.com/ads/library/?q=nike\&country=US), one object per source. | | `count` | `integer` | | Maximum CHARGED results. The actor refuses to run below 10 and bills this full number even when fewer ads are found, verified live 2026-07-30 (count 10 returned 3 ads and billed 10). Set it to what you are willing to pay for. | | `runTag` | `string` | | Free-form label copied into a runTag column on every output row. | | `limitPerSource` | `integer` | | Maximum ads per input URL. The actual number can exceed this by up to 30. Use count to bound the bill. | | `scrapeAdDetails` | `boolean` | | Also fetch ad detail data such as EU reach info. No extra charge event, but it makes runs slower. | | `scrapePageAds.period` | \`\`, `last24h`, `last7d`, `last14d`, `last30d` | | Date range filter applied when scraping ads from page URLs. Defaults to `""`. | | `scrapePageAds.sortBy` | `impressions_desc`, `most_recent` | | Sort page ads by impressions or recency. Defaults to `"impressions_desc"`. | | `scrapePageAds.countryCode` | `string` | | Uppercase ISO 3166-1 alpha-2 country code, or ALL. Defaults to `"ALL"`. | | `scrapePageAds.activeStatus` | `all`, `active`, `inactive` | | Filter page ads by active status. Defaults to `"all"`. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"facebook.ads","input":{"urls":["example"],"count":10}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "url": "https://facebook.com/example-account", "spend": null, "page_id": "309796912374932", "currency": "", "end_date": 1782457200, "position": 1, "snapshot": { "body": { "text": "{{product.brand}}" }, "cards": [ { "body": "See how Zapier can give you more time for what matters most. Start automating your work for free, no code required.", "title": "Start automating your work today", "cta_text": "Sign Up", "cta_type": "SIGN_UP", "link_url": "https://zapier.com/" } ], "title": "{{product.name}}", "images": [], "videos": [], "cta_text": "Sign Up", "cta_type": "SIGN_UP", "link_url": "https://zapier.com/", "page_name": "Zapier", "display_format": "DCO", "page_categories": [ "Business" ], "page_profile_uri": "https://facebook.com/example-account/" }, "ads_count": 1, "is_active": false, "page_name": "Zapier", "categories": [ "UNKNOWN" ], "start_date": 1745305200, "ad_archive_id": "1736224493918475", "ad_library_url": "https://facebook.com/example-account/library/?id=1736224493918475", "reach_estimate": null, "collation_count": 1, "end_date_formatted": "2026-06-26 07:00:00", "publisher_platform": [ "FACEBOOK" ], "start_date_formatted": "2025-04-22 07:00:00" } ``` # Facebook post comments Source: https://docs.usegoro.ai/tools/social/facebook-comments Comments and threaded replies on public Facebook posts, reels and videos, with comment text, author name and profile, timestamp, like count and threading depth. Comments and threaded replies on public Facebook posts, reels and videos, with comment text, author name and profile, timestamp, like count and threading depth. Use to read the conversation under a specific Facebook post: sentiment, objections, questions, or who is engaging. Needs post URLs, not page URLs. Every reply counts as its own billed comment when includeNestedComments is on. ## Price **\$0.0060 per comment.** That is the rate you pay for each comment the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer comments costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ----------------------- | --------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `startUrls` | `array` | yes | Facebook post, reel or video URLs, one object per post. | | `viewOption` | `RANKED_THREADED`, `RECENT_ACTIVITY`, `RANKED_UNFILTERED` | | Comment sort order: most relevant, newest, or all. Defaults to `"RANKED_UNFILTERED"`. | | `resultsLimit` | `integer` | | Maximum comments to return per post. Leave unset to return as many as possible, which can be thousands and is billed per comment. | | `includeNestedComments` | `boolean` | | Return up to 3 levels of replies. Each reply is a separate result and is billed as its own comment. Defaults to `false`. | | `onlyCommentsNewerThan` | `string` | | COSTS EXTRA. YYYY-MM-DD, full ISO, or relative like "3 days". Apify bills an additional filter-applied event of \$0.001 per post scraped with a date filter. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"facebook.comments","input":{"startUrls":["example"],"resultsLimit":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "date": "2021-08-15T20:11:20.000Z", "text": "This is exactly what our neighbourhood needed. Great to see it happening.", "inputUrl": "https://facebook.com/example-account/posts/pfbid0BbKbkisExKGSKuhee9a7i86", "commentId": "sample-id-000", "postTitle": "(2/3) \"I knew nothing about gardening. But I knew how to google.\"", "profileId": "sample-id-000", "commentUrl": "https://facebook.com/example-account/posts/pfbid02F3KGSGECuu3q8Cu2ey4TDG", "likesCount": "92", "facebookUrl": "https://facebook.com/example-account/posts/pfbid0BbKbkisExKGSKuhee9a7i86", "profileName": "Laurie Davis", "profilePicture": "https://example.com/media/sample.jpg", "threadingDepth": 0 } ``` # Facebook events Source: https://docs.usegoro.ai/tools/social/facebook-events Structured Facebook event records: name, description, start time and duration, going, interested and responded counts, venue with address, organizers, ticket li Structured Facebook event records: name, description, start time and duration, going, interested and responded counts, venue with address, organizers, ticket links and event type flags. Use to pull details for specific Facebook events. Pass event URLs in startUrls, which is the reliable path. Keyword discovery through searchQueries is logged out and frequently returns no results, and a no-result run is still billed one event. ## Price **\$0.03 per event.** That is the rate you pay for each event the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer events costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | --------------- | --------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `maxEvents` | `integer` | | Maximum events to extract. Leave unset for no limit. Billed per event at the highest per-unit price in this catalog. | | `startUrls` | `array` | | Plain URL strings, not objects. Event detail URLs such as [https://www.facebook.com/events/1023978871819924](https://www.facebook.com/events/1023978871819924) work best. Search and explore URLs are accepted but often return nothing. | | `searchQueries` | `array` | | Topic and/or place queries such as "Sport New York". Unreliable while logged out: verified 2026-07-30 returning no results for several queries, and the run is still billed one event. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"facebook.events","input":{"maxEvents":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "1023978871819924", "url": "https://facebook.com/example-account/1023978871819924/", "name": "Mia Fontaine", "isPast": true, "duration": "4 hr", "imageUrl": "https://example.com/media/sample.jpg", "inputUrl": "https://facebook.com/example-account/1023978871819924", "isOnline": false, "location": { "id": "215609511542", "url": "https://facebook.com/example-account", "name": "Tomas Novak" }, "eventType": "PUBLIC", "startTime": "Thu, 2 Feb 2023", "isCanceled": false, "usersGoing": 4537, "organizedBy": "Event by Live Nation Australia, Post Malone and Red Hot Chili Peppers", "utcStartDate": "2023-02-02T09:00:00.000Z", "externalLinks": [ "http://telstra.com/tickets", "http://lvntn.com/RHCP23" ], "eventFrequency": "SINGLE", "usersResponded": 17528, "usersInterested": 12991, "dateTimeSentence": "Thursday 2 February 2023 from 20:00-00:00 AEDT" } ``` # Facebook group posts Source: https://docs.usegoro.ai/tools/social/facebook-group_posts Posts from public Facebook groups with post text, author, permalink, timestamp, attachments and reaction, comment and share counts. Posts from public Facebook groups with post text, author, permalink, timestamp, attachments and reaction, comment and share counts. Use to mine a public Facebook group for demand signals, recurring questions, buy and sell listings or community sentiment. Only public groups can be scraped. ## Price **\$0.0120 per post.** That is the rate you pay for each post the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer posts costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | -------------------- | ------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `startUrls` | `array` | yes | Public Facebook group URLs, one object per group. Private groups cannot be scraped. | | `viewOption` | `CHRONOLOGICAL`, `RECENT_ACTIVITY`, `TOP_POSTS`, `CHRONOLOGICAL_LISTINGS` | | Sort order. New posts, newest activity, most relevant, or BuySell listings. resultsLimit applies to new posts only. Defaults to `"CHRONOLOGICAL"`. | | `resultsLimit` | `integer` | | Maximum posts to return per group. Leave unset to return as many as possible, billed per post. | | `searchGroupYear` | `string` | | Restrict a keyword search to one year. Requires searchGroupKeyword. | | `onlyPostsNewerThan` | `string` | | COSTS EXTRA. YYYY-MM-DD, full ISO, or relative like "2 months". Apify bills an additional filter-applied event of \$0.001 per post scraped with a date filter. | | `searchGroupKeyword` | `string` | | Search inside the group. Without a login this only works reliably with a one or two letter query. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"facebook.group_posts","input":{"startUrls":["example"],"resultsLimit":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "UzpfSTEwMDA2NDM5NjI4NTkyMTpWSzoyNTY3OTY0MDczNjk4MDAx", "url": "https://facebook.com/example-account/874728723021553/permalink/2567964073698001/", "text": "Warm welcome to everyone who joined this week.", "time": "2026-07-30T12:42:30.000Z", "user": { "id": "100064396285921", "name": "Priya Raman", "profilePic": "https://example.com/media/sample.jpg" }, "inputUrl": "https://facebook.com/example-account/874728723021553", "legacyId": "2567964073698001", "groupTitle": "Maths Formulas", "likesCount": 3, "attachments": [ { "thumbnail": "https://example.com/media/sample.jpg" } ], "facebookUrl": "https://facebook.com/example-account/874728723021553", "sharesCount": 0, "commentsCount": 0, "paidPartnership": false, "reactionLoveCount": 3, "topReactionsCount": 1 } ``` # Facebook page profile Source: https://docs.usegoro.ai/tools/social/facebook-page Public profile data for Facebook pages: title, category, likes and follower counts, intro text, contact email, website, creation date, ad status and photo URLs. Public profile data for Facebook pages: title, category, likes and follower counts, intro text, contact email, website, creation date, ad status and photo URLs. Use to enrich a business or brand from its Facebook page, for audience size, contact details, category and website. Works on pages only, never on personal profiles. One page is billed per URL you pass. ## Price **\$0.0246 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ----------- | ------- | -------- | ---------------------------------------------------------------------------------------------------- | | `startUrls` | `array` | yes | Facebook page URLs, one object per page. Pages only, not personal profiles. Billed one page per URL. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"facebook.page","input":{"startUrls":["example"]}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "email": "contact@example.com", "intro": "Explore the universe and discover our home planet.", "likes": 28660274, "title": "NASA - National Aeronautics and Space Administration", "pageId": "100044561550831", "website": "https://www.nasa.gov/nasa-app/", "category": "Government organization", "pageName": "NASA", "websites": [ "https://www.nasa.gov/nasa-app/", "https://www.nasa.gov/" ], "ad_status": "This Page isn't currently running ads.", "followers": 28660274, "categories": [ "Page", "Government organization" ], "followings": 52, "facebookUrl": "https://facebook.com/example-account/", "coverPhotoUrl": "https://example.com/media/sample.jpg", "creation_date": "March 4, 2009", "profilePictureUrl": "https://example.com/media/sample.jpg" } ``` # Facebook profile posts Source: https://docs.usegoro.ai/tools/social/facebook-profile_posts Posts from a public Facebook profile or page feed, with message text, permalink, timestamp, per-type reaction breakdown, comment and share counts, images and vi Posts from a public Facebook profile or page feed, with message text, permalink, timestamp, per-type reaction breakdown, comment and share counts, images and video files. Also resolves profile details and profile IDs. Use to pull a public profile's or page's recent feed, or to resolve a profile URL to its numeric ID. Public targets only. Pick an endpoint first, then fill the matching text field: urls\_text for the by-URL endpoints, ids\_text for the by-ID endpoints, keywords\_text for keyword search. ## Price **\$0.0180 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | --------------- | -------------------------------------------------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `end_date` | `string` | | Optional YYYY-MM-DD upper bound for post and keyword endpoints. | | `endpoint` | `profile_posts_by_url`, `profile_posts`, `search_posts_by_keyword`, `details_by_id`, `details_by_url`, `profile_id_by_url` | yes | What to fetch. profile\_posts\_by\_url and profile\_posts return feeds, details\_\* return profile metadata, profile\_id\_by\_url resolves a URL to an ID. | | `ids_text` | `string` | | Newline-separated numeric profile IDs. Used by profile\_posts and details\_by\_id. | | `max_posts` | `integer` | | Maximum posts per target. 0 means no limit. Every returned row is billed, and the by-URL endpoints also emit one extra billed row per target that carries the resolved profile ID. | | `urls_text` | `string` | | Newline-separated profile or page URLs. Used by profile\_posts\_by\_url, details\_by\_url and profile\_id\_by\_url. Note this is a STRING with one URL per line, not an array. | | `start_date` | `string` | | Optional YYYY-MM-DD lower bound for post and keyword endpoints. | | `keywords_text` | `string` | | Newline-separated search queries. Used by search\_posts\_by\_keyword. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"facebook.profile_posts","input":{"endpoint":"profile_posts_by_url","max_posts":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "profile_id": "sample-id-000", "url (search term)": "https://facebook.com/example-account/100063663337621/" } ``` # Facebook page reviews Source: https://docs.usegoro.ai/tools/social/facebook-reviews Recommendations and reviews left on a public Facebook page, with review text, reviewer, date, recommended or not, attribute tags and engagement counts. Recommendations and reviews left on a public Facebook page, with review text, reviewer, date, recommended or not, attribute tags and engagement counts. Use to read what customers say about a business on Facebook: reputation checks, review mining, competitor research. Pass the page's reviews URL in the form facebook.com/pagename/reviews. ## Price **\$0.0060 per review.** That is the rate you pay for each review the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer reviews costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | -------------- | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | `startUrls` | `array` | yes | Facebook page review URLs, in the form [https://www.facebook.com/pagename/reviews](https://www.facebook.com/pagename/reviews), one object per page. | | `resultsLimit` | `integer` | | Maximum reviews to return per page. Leave unset to return as many as possible, billed per review. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"facebook.reviews","input":{"startUrls":["example"],"resultsLimit":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "UzpfSTEwMDAwODg0Mjg0MTAzMDozNDMzNTYyNDMzNjE1MTUx", "url": "https://facebook.com/example-account/posts/pfbid0bZAHZYxvvsoX1cVRMsoySYX", "date": "2024-09-11T17:57:58.000Z", "tags": [ "Child-friendly", "Fast delivery", "Cosy atmosphere" ], "text": "They offer a cozy atmosphere, friendly service, and a menu of tasty comfort food at reasonable prices.", "user": { "id": "100008842841030", "name": "Sam Okafor", "profileUrl": "https://facebook.com/example-account" }, "inputUrl": "https://facebook.com/example-account/reviews", "legacyId": "3433562433615151", "pageName": "copperkettleyqr", "likesCount": 0, "facebookUrl": "https://facebook.com/example-account", "commentsCount": 0, "isRecommended": true } ``` # Instagram hashtag Source: https://docs.usegoro.ai/tools/social/instagram-hashtag Posts or reels carrying a given Instagram hashtag, with captions, engagement counts, and author handles. Posts or reels carrying a given Instagram hashtag, with captions, engagement counts, and author handles. Use when you need to track a hashtag or campaign, find creators posting about a topic, or gauge how much traction a tag is getting. ## Price **\$0.0069 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | --------------- | --------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `hashtags` | `array` | yes | Hashtag or keyword to scrape. One per call: resultsLimit applies per hashtag, so batching several would bill past this request's hold. A leading # is optional. | | `resultsType` | `posts`, `reels`, `stories` | | Defaults to `"posts"`. | | `resultsLimit` | `integer` | | Maximum results per hashtag or keyword Defaults to `20`. | | `keywordSearch` | `boolean` | | Treat entries as free-text keywords rather than hashtags | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"instagram.hashtag","input":{"hashtags":["example"],"resultsLimit":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "3401234567890123457", "url": "https://instagram.com/example-account/DLMabcXYZ12/", "type": "Video", "likes": 9821, "caption": "morning routine that actually stuck #productivity", "comments": 231, "shortcode": "DLMabcXYZ12", "timestamp": "2026-07-02T08:30:00.000Z", "display_url": "https://example.com/media/sample.jpg", "owner_username": "dailyhabits" } ``` # Instagram posts Source: https://docs.usegoro.ai/tools/social/instagram-posts Recent posts from one Instagram profile with captions, engagement counts, media URLs, and timestamps. Recent posts from one Instagram profile with captions, engagement counts, media URLs, and timestamps. Use when you need a creator's or brand's recent Instagram content, to measure engagement over time, or to pull captions and hashtags for analysis. ## Price **\$0.0150 per post.** That is the rate you pay for each post the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer posts costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | -------------------- | --------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `username` | `array` | yes | Instagram username, profile URL, or post URL. One target per call: resultsLimit applies per target, so batching several here would bill past the hold sized for this request. | | `resultsLimit` | `integer` | | Maximum posts per profile Defaults to `20`. | | `dataDetailLevel` | `basicData`, `detailedData` | | detailedData is billed extra at \$0.008 per post Defaults to `"basicData"`. | | `skipPinnedPosts` | `boolean` | | Defaults to `false`. | | `onlyPostsNewerThan` | `string` | | YYYY-MM-DD, ISO datetime, or relative like "3 months" | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"instagram.posts","input":{"username":["example"],"resultsLimit":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "3401234567890123456", "url": "https://instagram.com/example-account/DLNsnpUTdVS/", "type": "Image", "likes": 184203, "caption": "Photograph by @thomaspeschak", "comments": 1204, "shortcode": "DLNsnpUTdVS", "timestamp": "2026-07-01T12:00:00.000Z", "display_url": "https://example.com/media/sample.jpg", "owner_username": "natgeo" } ``` # Instagram profile Source: https://docs.usegoro.ai/tools/social/instagram-profile Public profile data for one or more Instagram accounts: follower and following counts, bio, verification, external link, and post count. Public profile data for one or more Instagram accounts: follower and following counts, bio, verification, external link, and post count. Use when you need an Instagram account's stats or bio, to check whether a handle is real or verified, or to size a creator's audience before outreach. ## Price **\$0.0069 per profile.** That is the rate you pay for each profile the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer profiles costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | --------------------- | --------- | -------- | --------------------------------------------------------------------------------------------------------------- | | `usernames` | `array` | yes | Instagram usernames or user IDs to scrape | | `includeAboutSection` | `boolean` | | Also extract about-account info (join date, country). Billed extra at \$0.006 per account. Defaults to `false`. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"instagram.profile","input":{"usernames":["example"]}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "url": "https://instagram.com/example-account/", "follows": 150, "private": false, "username": "example_user", "verified": true, "biography": "Experience the world through the eyes of National Geographic photographers.", "followers": 278000000, "full_name": "Priya Raman", "is_business": true, "posts_count": 29500, "external_url": "https://on.natgeo.com/instagram", "profile_pic_url": "https://example.com/media/sample.jpg" } ``` # Instagram search Source: https://docs.usegoro.ai/tools/social/instagram-search Search Instagram by keyword for hashtags, profiles, or places. Each result carries a type field, and the shape follows it: hashtags return a post count, profile Search Instagram by keyword for hashtags, profiles, or places. Each result carries a type field, and the shape follows it: hashtags return a post count, profiles return follower stats, places return an address and coordinates. Use when you need to find Instagram accounts or hashtags for a topic and do not already know the handle, for example building a creator shortlist. ## Price **\$0.0069 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ----------------------------------- | ------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------- | | `search` | `string` | yes | Keyword to search. Comma-separate for multiple terms. | | `liveSearch` | `boolean` | | Use live Instagram search; returns a slightly different shape | | `searchType` | `place`, `user`, `hashtag`, `popular` | | What to search for. The result shape follows this. Defaults to `"hashtag"`. | | `searchLimit` | `integer` | | Maximum results per search term Defaults to `10`. | | `enhanceUserSearchWithFacebookPage` | `boolean` | | Enrich the top 10 users with Facebook page and business email. Higher cost, and personal data: check your jurisdiction. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"instagram.search","input":{"search":"example","searchLimit":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "coffeelover", "url": "https://instagram.com/example-account/tags/coffeelover", "name": "example_user", "type": "hashtag", "posts_count": 12345000 } ``` # Instagram URL Source: https://docs.usegoro.ai/tools/social/instagram-url Scrape any public Instagram URL: a profile, post, or reel. Returns posts, comments, reels, or profile details depending on what you ask for. Scrape any public Instagram URL: a profile, post, or reel. Returns posts, comments, reels, or profile details depending on what you ask for. Use when you already have an Instagram URL and want its contents, especially post comments, which the other Instagram tools do not return. ## Price **\$0.0060 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | -------------------- | -------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `search` | `string` | | Search Instagram instead of using directUrls. Billed at $0.005 per result rather than $0.002. | | `directUrls` | `array` | | Instagram profile, post, or reel URL. One per call: resultsLimit applies per URL, so batching several would bill past this request's hold. | | `searchType` | `user`, `hashtag`, `place` | | Defaults to `"hashtag"`. | | `resultsType` | `posts`, `comments`, `details`, `mentions`, `reels`, `stories` | | Comments only work on post URLs Defaults to `"posts"`. | | `searchLimit` | `integer` | | Defaults to `1`. | | `resultsLimit` | `integer` | | Defaults to `20`. | | `addParentData` | `boolean` | | Defaults to `false`. | | `onlyPostsNewerThan` | `string` | | | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"instagram.url","input":{"resultsLimit":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "18031234567890123", "url": "https://instagram.com/example-account/DLNsnpUTdVS/", "type": "Image", "likes": 184203, "caption": "Photograph by @thomaspeschak", "comments": 1204, "timestamp": "2026-07-01T12:00:00.000Z", "display_url": "https://example.com/media/sample.jpg", "owner_username": "natgeo" } ``` # LinkedIn post search Source: https://docs.usegoro.ai/tools/social/linkedin-post_search Searches LinkedIn posts by keyword with filters for recency, content type, author, author employer, and mentions. Returns post text, media, engagement counts, a Searches LinkedIn posts by keyword with filters for recency, content type, author, author employer, and mentions. Returns post text, media, engagement counts, and the author's profile summary. Use to monitor what is being said about a topic, product, or company on LinkedIn, to find people posting about a problem you solve, or to track a competitor's reach. For one specific person's or company's feed, use linkedin.profile\_posts instead. ## Price **\$0.0060 per post.** That is the rate you pay for each post the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer posts costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ----------------- | --------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `sortBy` | `relevance`, `date` | | Result ordering. | | `maxPosts` | `integer` | | Maximum posts per search query. This is the field the price quote is sized from. Defaults to `20`. | | `startPage` | `integer` | | Search page to start from. Defaults to `1`. | | `authorUrls` | `array` | | Restrict to posts by these LinkedIn profile or company URLs. | | `contentType` | `all`, `videos`, `images`, `jobs`, `live_videos`, `documents`, `collaborative_articles` | | Restrict to one media type. | | `maxComments` | `integer` | | Comments per post when scrapeComments is on. Defaults to 10, and each one is billed. | | `postedLimit` | `any`, `1h`, `24h`, `week`, `month`, `3months`, `6months`, `year` | | Only posts newer than this. | | `scrapePages` | `integer` | | Number of search pages, 100 posts each. Raising this above maxPosts/100 raises the real cost without raising the quote, so prefer maxPosts. | | `maxReactions` | `integer` | | Reactions per post when scrapeReactions is on. Defaults to 10, and each one is billed. | | `searchQueries` | `array` | yes | Keyword queries, exactly as you would type them into the LinkedIn search bar. maxPosts applies to EACH query, so two queries can cost twice the quote. | | `authorKeywords` | `string` | | Only authors whose headline or job title contains one of these keywords. | | `scrapeComments` | `boolean` | | Also pull each post's comments. Billed EXTRA at \$0.002 per comment, and comments arrive as their own dataset items. Defaults to `false`. | | `postedLimitDate` | `string` | | Only posts newer than this date, for example "2026-01-10" or an ISO datetime. | | `scrapeReactions` | `boolean` | | Also pull each post's reactions. Billed EXTRA at \$0.002 per reaction, and reactions arrive as their own dataset items, so a 20-post run with 10 reactions each bills 200 extra events. Defaults to `false`. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"linkedin.post_search","input":{"searchQueries":["example"],"maxPosts":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "7488389022264774656", "type": "post", "query": { "page": 1, "search": "b2b sales" }, "author": { "id": "ACoAAsampleProfileId00000000000000000", "name": "Alex Rivera", "type": "profile", "linkedinUrl": "https://linkedin.com/example-account", "publicIdentifier": "danielfoos" }, "content": "Join a winning sales team in Phoenix, AZ! AT&T is hiring a B2B Sales Account Executive with a $4,500 sign-on bonus.", "postedAt": { "date": "2026-07-30T00:24:30.448Z", "timestamp": 1785371070448, "postedAgoShort": "14h" }, "commentIds": [], "engagement": { "likes": 2, "shares": 0, "comments": 0, "reactions": [ { "type": "LIKE", "count": 2 } ] }, "postImages": [], "linkedinUrl": "https://linkedin.com/example-account/activity-7488389022264774656-lpwt", "reactionIds": [] } ``` # LinkedIn profile posts Source: https://docs.usegoro.ai/tools/social/linkedin-profile_posts Recent posts from specific LinkedIn profiles or company pages, with text, media, engagement counts, and reposts. Reactions and comments can be pulled per post a Recent posts from specific LinkedIn profiles or company pages, with text, media, engagement counts, and reposts. Reactions and comments can be pulled per post as a paid extra. Use to read one person's or company's LinkedIn feed: to research a prospect before outreach, track a competitor's announcements, or measure how a creator's posts perform over time. ## Price **\$0.0060 per post.** That is the rate you pay for each post the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer posts costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | --------------------- | ----------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `maxPosts` | `integer` | | Maximum posts per profile or company. This is the field the price quote is sized from. Do not send 0, which means every post ever. Defaults to `10`. | | `targetUrls` | `array` | yes | LinkedIn profile URLs, company URLs, or single post URLs. maxPosts applies to EACH entry, so three profiles can cost three times the quote. | | `maxComments` | `integer` | | Comments per post when scrapeComments is on. Defaults to 5, and each one is billed. | | `postedLimit` | `any`, `1h`, `24h`, `week`, `month`, `3months`, `6months`, `year` | | Only posts newer than this. | | `maxReactions` | `integer` | | Reactions per post when scrapeReactions is on. Defaults to 5, and each one is billed. | | `contextCountry` | `any`, `US`, `GB`, `DE`, `FR` | | Country context LinkedIn serves the feed from. Affects which posts are visible. | | `includeReposts` | `boolean` | | Include plain reposts. Defaults to `true`. | | `scrapeComments` | `boolean` | | Also pull each post's comments. Billed EXTRA at \$0.002 per comment, and comments arrive as their own dataset items. Defaults to `false`. | | `postedLimitDate` | `string` | | Only posts newer than this date, for example "2026-01-10" or an ISO datetime. | | `scrapeReactions` | `boolean` | | Also pull each post's reactions. Billed EXTRA at \$0.002 per reaction, and reactions arrive as their own dataset items. Defaults to `false`. | | `includeQuotePosts` | `boolean` | | Include shared posts that carry a comment. Defaults to `true`. | | `postNestedComments` | `boolean` | | Legacy. Nests comments inside the post item instead of emitting them separately. Not recommended. Defaults to `false`. | | `commentsPostedLimit` | `any`, `1h`, `24h`, `week`, `month` | | Only comments newer than this. | | `postNestedReactions` | `boolean` | | Legacy. Nests reactions inside the post item instead of emitting them separately. Not recommended. Defaults to `false`. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"linkedin.profile_posts","input":{"targetUrls":["example"],"maxPosts":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "7489012345678901234", "type": "post", "query": { "targetUrl": "https://linkedin.com/example-account/" }, "author": { "id": "ACoAAsampleProfileId00000000000000000", "name": "Alex Rivera", "type": "profile", "linkedinUrl": "https://linkedin.com/example-account", "publicIdentifier": "satyanadella" }, "content": "Excited to share what our team shipped this quarter.", "postedAt": { "date": "2026-07-29T09:12:11.000Z", "timestamp": 1785371070448, "postedAgoShort": "1d" }, "commentIds": [], "engagement": { "likes": 4821, "shares": 91, "comments": 216, "reactions": [ { "type": "LIKE", "count": 4102 }, { "type": "PRAISE", "count": 719 } ] }, "postImages": [], "linkedinUrl": "https://linkedin.com/example-account/activity-7489012345678901234-Ab3d", "reactionIds": [] } ``` # Reddit API search Source: https://docs.usegoro.ai/tools/social/reddit-api Reddit posts, comments, communities and users read through Reddit's own API, returning title, body, HTML, upvotes, comment count, author and community for each Reddit posts, comments, communities and users read through Reddit's own API, returning title, body, HTML, upvotes, comment count, author and community for each post. Use as the fast structured Reddit reader for a search query, a subreddit feed, a single post URL or a user profile. Each query, subreddit or URL is a separate job, and a search job always fetches and bills a full page of 25 items, so asking for fewer does not cost less. ## Price **\$0.0090 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ------------------- | ----------------------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | `sort` | `relevance`, `hot`, `new`, `top`, `comments`, `controversial`, `rising` | | relevance and comments are search-only. Subreddit feeds use hot, new, top, controversial or rising Defaults to `"relevance"`. | | `time` | `all`, `day`, `hour`, `month`, `week`, `year` | | Defaults to `"all"`. | | `maxItems` | `integer` | | Maximum results per job. A search job always fetches and bills a full page of 25 items, so values below 25 do not cost less Defaults to `25`. | | `searches` | `array` | | Search queries. Each query is a separate billed job. Wrap in quotes for an exact phrase | | `startUrls` | `array` | | Reddit subreddit, post or user URLs. Each URL is a separate billed job | | `includeNSFW` | `boolean` | | Defaults to `false`. | | `searchPosts` | `boolean` | | Defaults to `true`. | | `searchUsers` | `boolean` | | Defaults to `false`. | | `skipComments` | `boolean` | | For a direct post URL, return only the post without its comments Defaults to `false`. | | `skipCommunity` | `boolean` | | Defaults to `true`. | | `skipUserPosts` | `boolean` | | Defaults to `false`. | | `searchComments` | `boolean` | | Defaults to `false`. | | `ignorestartUrls` | `boolean` | | Forces search-only mode: startUrls are ignored even when present. Note the lowercase s in the middle, which is how the actor spells it. | | `fetchPostComments` | `boolean` | | Also attach top-level comments to each post. One extra request per post and materially more billed items Defaults to `false`. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"reddit.api","input":{"maxItems":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "t3_1rxssfj", "url": "https://www.reddit.com/r/apify/comments/1rxssfj/example_post_title/", "body": "Hey everyone, the full selftext of the post.", "html": "<div class=\"md\"><p>Hey everyone</p></div>", "isAd": false, "title": "Example post title", "over18": false, "isVideo": false, "upVotes": 11, "dataType": "post", "parsedId": "1rxssfj", "username": "example_user", "createdAt": "2026-03-19T06:20:58.000Z", "scrapedAt": "2026-07-30T14:34:01.975Z", "communityName": "r/apify", "numberOfComments": 16, "parsedCommunityName": "apify" } ``` # Reddit posts Source: https://docs.usegoro.ai/tools/social/reddit-posts Reddit posts and their comments from a search term, subreddit, user profile or direct post URL, with title, body, community, author, timestamps and optionally u Reddit posts and their comments from a search term, subreddit, user profile or direct post URL, with title, body, community, author, timestamps and optionally upvotes and media links. Use for broad Reddit research where you want posts together with their comment threads from public pages. This actor charges a fixed \$0.04 start fee before the first result, so send one larger request rather than many tiny ones. ## Price **\$0.0114 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | --------------- | ------------------------------------------------------ | -------- | --------------------------------------------------------------------------------------------------------------------------- | | `sort` | `relevance`, `hot`, `top`, `new`, `rising`, `comments` | | Defaults to `"new"`. | | `time` | `all`, `hour`, `day`, `week`, `month`, `year` | | | | `maxItems` | `integer` | | Maximum items saved across the whole run. Billed per item, on top of the fixed \$0.04 start fee Defaults to `10`. | | `searches` | `array` | | Search terms to run against Reddit. Remove startUrls when you use this | | `startUrls` | `array` | | Reddit post, subreddit, user or search URLs. Leave empty when you use searches | | `includeNSFW` | `boolean` | | Defaults to `false`. | | `maxComments` | `integer` | | Maximum comments taken from each post page. Set to 0 to skip comments. Comments count as billed items too Defaults to `10`. | | `searchMedia` | `boolean` | | Also search media posts for the search terms. | | `searchPosts` | `boolean` | | Defaults to `true`. | | `searchUsers` | `boolean` | | Defaults to `false`. | | `maxPostCount` | `integer` | | Maximum posts taken from each posts page or community page Defaults to `10`. | | `maxUserCount` | `integer` | | Pages of user results to scrape. The actor takes 2 when you leave it out. Each page is more billed items. | | `skipComments` | `boolean` | | Do not scrape comments while going through posts Defaults to `false`. | | `postDateLimit` | `string` | | Only posts published after this date | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"reddit.posts","input":{"maxItems":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "t3_1rxssfj", "url": "https://www.reddit.com/r/programming/comments/1rxssfj/example_post_title/", "body": "The full selftext of the post.", "isAd": false, "title": "Example post title", "over18": false, "userId": "sample-id-000", "isVideo": false, "upVotes": 11, "dataType": "post", "parsedId": "1rxssfj", "username": "example_user", "createdAt": "2026-07-29T06:20:58.000Z", "scrapedAt": "2026-07-30T14:34:01.975Z", "communityName": "r/programming", "numberOfComments": 16, "parsedCommunityName": "programming" } ``` # TikTok Source: https://docs.usegoro.ai/tools/social/tiktok-api TikTok data through TikTok's own endpoints: a profile and its stats, a user's posts and likes, posts for a hashtag, keyword search across posts, users, sounds, TikTok data through TikTok's own endpoints: a profile and its stats, a user's posts and likes, posts for a hashtag, keyword search across posts, users, sounds, hashtags and lives, comments and replies, followers and following, a single post, and watermark free download links. Use for anything on TikTok: a creator's recent posts, what is trending for a hashtag, search results for a keyword, comments on a video, or a follower list. Fill in only the sections you want. Each section is one flat \$0.002 request no matter how many results it returns, so ask for a large count in one call rather than paging with many small ones. Posts endpoints take a user id, not a username: send usernameToId\_username first, or in the same call, to resolve one. ## Price **\$0.0060 per request.** That is the rate you pay for each request the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer requests costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ------------------ | --------- | -------- | ---------------------------------------------------------------------- | | `music_id` | `string` | | Music track lookup by ID. | | `post_region` | `string` | | Two letter region for the post lookup. Defaults to GB. | | `post_awemeId` | `string` | | Single post lookup by aweme\_id. | | `profile_region` | `string` | | Two letter region for the profile lookup. Defaults to GB. | | `profile_userId` | `string` | | Profile lookup by numeric user ID. | | `followers_count` | `integer` | | Followers to return. Does NOT change the price: flat \$0.002 per call. | | `following_count` | `integer` | | Followed accounts to return. Does NOT change the price. | | `userLikes_count` | `integer` | | Liked posts to return. Does NOT change the price. | | `userPosts_count` | `integer` | | Posts to return. Does NOT change the price. | | `followers_userId` | `string` | | User ID to list followers for. | | `following_userId` | `string` | | User ID to list followed accounts for. | | `musicPosts_count` | `integer` | | Videos to return for that sound. Does NOT change the price. | | `profile_username` | `string` | | Profile lookup by username, without the @ sign, e.g. katyperry. | | `userLikes_userId` | `string` | | User ID whose liked posts to list. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"tiktok.api","input":{}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "user": { "uid": "7664638705177150477", "region": "US", "nickname": "NASA", "signature": "Making the seemingly impossible, possible.", "unique_id": "nasa", "aweme_count": 16, "follower_count": 329461, "following_count": 20, "favoriting_count": 0, "enterprise_verify_reason": "institution account" }, "extra": { "now": 1785422006000, "logid": "20260730143325341C26D769F440188C83" }, "status_msg": "", "status_code": 0 } ``` # TikTok profile posts Source: https://docs.usegoro.ai/tools/social/tiktok-profile Every post from one or more TikTok profiles at the cheapest per item price in the catalog, with views, likes, comments, shares, bookmarks, hashtags, the song, a Every post from one or more TikTok profiles at the cheapest per item price in the catalog, with views, likes, comments, shares, bookmarks, hashtags, the song, and the author's channel stats. Use when you want a creator's post history in bulk: tracking a roster of creators, building a content database, or measuring engagement over time. Use tiktok.posts instead when you need its richer per-post field set. ## Price **\$0.0009 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ----------- | --------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- | | `since` | `string` | | Only posts older than this date. Accepts YYYY-MM-DD. | | `until` | `string` | | Only posts newer than this date. Accepts YYYY-MM-DD. | | `maxItems` | `integer` | | Total posts to return across ALL targets, not per target. This is the field the price hold is sized from. Defaults to `100`. | | `startUrls` | `array` | | TikTok profile URLs, e.g. [https://www.tiktok.com/@nasa](https://www.tiktok.com/@nasa). An alternative to usernames. | | `usernames` | `array` | | TikTok usernames, without the @ sign. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"tiktok.profile","input":{"maxItems":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "7665075736742530317", "song": { "title": "original sound", "artist": "NASA" }, "likes": 47932, "title": "Something big just landed on TikTok.", "video": { "width": 720, "height": 1280, "duration": 25 }, "views": 382141, "shares": 2244, "channel": { "id": "7664638705177150477", "bio": "Making the seemingly impossible, possible.", "name": "Sam Okafor", "videos": 16, "username": "example_user", "verified": true, "followers": 329461, "following": 20 }, "comments": 1273, "hashtags": [], "postPage": "https://tiktok.com/example-account/video/7665075736742530317", "bookmarks": 3786, "uploadedAt": 1784664542, "inputSource": "nasa", "uploadedAtFormatted": "2026-07-21T20:09:02.000Z" } ``` # X followers and following Source: https://docs.usegoro.ai/tools/social/twitter-followers The follower list, and optionally the following list, of an X (Twitter) account, with each user's handle, display name, bio, location, follower and following co The follower list, and optionally the following list, of an X (Twitter) account, with each user's handle, display name, bio, location, follower and following counts, tweet and media counts, verification status, join date, and profile images. Use to map who follows an X account, to size and profile an audience, to find overlap between two accounts, or to build a lead list from a niche account's followers. One account per call, and set maxFollowers to how many you actually want since that is what you pay for. ## Price **\$0.000450 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | --------------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `user_ids` | `array` | | One numeric X user ID, as an alternative to user\_names. When set, user\_names is ignored. | | `user_names` | `array` | | One X handle, without the @ sign. One account per call: the counts below apply per account, so a second account would double the bill against the same hold. | | `getFollowers` | `boolean` | yes | Return the follower list. Defaults to `true`. | | `getFollowing` | `boolean` | yes | Also return the following list. This adds up to maxFollowings more billed items on top of the followers. Defaults to `false`. | | `maxFollowers` | `integer` | yes | How many followers to return. The actor rejects anything below 200. This is the field the price hold is sized from. Defaults to `200`. | | `maxFollowings` | `integer` | yes | How many followed accounts to return, only used when getFollowing is true. The actor rejects anything below 200. Keep this at or below maxFollowers, since the hold is sized from maxFollowers. Defaults to `200`. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"twitter.followers","input":{"maxFollowers":200,"maxFollowings":200,"getFollowers":true,"getFollowing":false}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": 100000000000000000, "url": null, "name": "Example Follower", "type": "follower", "id_str": "100000000000000001", "location": "", "verified": false, "protected": false, "created_at": "Mon Jan 01 12:00:00 +0000 2024", "description": "Example account used to show the shape of a follower record.", "media_count": 2, "screen_name": "example_follower", "listed_count": 0, "friends_count": 555, "statuses_count": 12, "followers_count": 1, "target_username": "example_account", "favourites_count": 126, "profile_banner_url": "https://example.com/banners/example_follower", "profile_image_url_https": "https://example.com/avatars/example_follower_normal.jpg" } ``` # X profile Source: https://docs.usegoro.ai/tools/social/twitter-profile Public profile data for one or more X (Twitter) accounts: display name, bio, follower and following counts, tweet and media counts, verification status and type Public profile data for one or more X (Twitter) accounts: display name, bio, follower and following counts, tweet and media counts, verification status and type, location, website, join date, and profile and header images. Use when you have X handles and need the accounts behind them: sizing an audience before outreach, checking whether a handle is real, verified or protected, or enriching handles you collected elsewhere. One result, and one billable item, per handle. ## Price **\$0.0006 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ----------- | ------- | -------- | ----------------------------------------------------------------------------- | | `usernames` | `array` | yes | X handles, without the @ sign. One result, and one billable item, per handle. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"twitter.profile","input":{"usernames":["example"]}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "1234567890", "desc": "Building tools for people who ship. Occasional threads about deep space.", "name": "Priya Raman", "avatar": "https://example.com/media/sample-avatar.jpg", "status": "active", "friends": 312, "profile": "example_user", "rest_id": "1234567890", "website": "https://example.com", "location": "Lisbon, Portugal", "protected": false, "sub_count": 48213, "affiliates": [], "created_at": "Mon Mar 04 11:22:19 +0000 2013", "media_count": 884, "header_image": "https://example.com/media/sample-header.jpg", "blue_verified": true, "statuses_count": 5107, "business_account": { "affiliates_count": 2 }, "verification_type": "Business", "pinned_tweet_ids_str": [ "1234567890123456789" ] } ``` # Twitter search Source: https://docs.usegoro.ai/tools/social/twitter-search Search recent tweets on X (Twitter) by keyword, hashtag, or advanced query and get back tweet text, author, and engagement metrics. Search recent tweets on X (Twitter) by keyword, hashtag, or advanced query and get back tweet text, author, and engagement metrics. Use when you need tweets about a topic, sentiment or chatter on X, mentions of a brand or person, or what people are saying right now. ## Price **\$0.0012 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | -------------- | --------------- | -------- | --------------------------------------------------------------------------------------------------------------- | | `end` | `string` | | Only tweets before this date, YYYY-MM-DD | | `sort` | `Top`, `Latest` | | Rank by relevance (Top) or recency (Latest) Defaults to `"Latest"`. | | `start` | `string` | | Only tweets after this date, YYYY-MM-DD | | `author` | `string` | | Only tweets sent by this handle. PERSONAL DATA: this points the search at one named person. | | `geocode` | `string` | | Only tweets from users inside a radius of a coordinate, as latitude,longitude,radius, e.g. 37.781,-122.398,1mi. | | `maxItems` | `integer` | | Maximum tweets to return across all search terms Defaults to `20`. | | `inReplyTo` | `string` | | Only tweets that are replies to this handle. | | `onlyImage` | `boolean` | | Only tweets containing an image. | | `onlyQuote` | `boolean` | | Only quote tweets. | | `onlyVideo` | `boolean` | | Only tweets containing video. | | `startUrls` | `array` | | X URLs to scrape directly instead of searching. Tweet, profile, search and list URLs all work. | | `mentioning` | `string` | | Only tweets mentioning this handle. | | `searchTerms` | `array` | yes | Search queries. Supports X advanced search syntax. | | `withinRadius` | `string` | | Radius around geotaggedNear, e.g. 15km. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"twitter.search","input":{"searchTerms":["example"],"maxItems":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "1811230000000000000", "url": "https://x.com/example-account/status/1811230000000000000", "text": "AI agents are eating the software stack", "likes": 42, "replies": 3, "retweets": 7, "created_at": "2026-07-01T12:00:00Z", "author_name": "Builder", "author_handle": "builder" } ``` # YouTube channel search Source: https://docs.usegoro.ai/tools/social/youtube-channel_search YouTube channels matching a topic keyword, ranked by YouTube's own channel search, each with subscriber count, lifetime views, video count, creation date, descr YouTube channels matching a topic keyword, ranked by YouTube's own channel search, each with subscriber count, lifetime views, video count, creation date, description and topic tags. Use when you need to find WHO covers a topic on YouTube and do not have a handle yet: building a creator shortlist, mapping who owns a niche, or sizing a competitive field before you commit. Returns channels, not videos, so it does not reward whoever titled one video well. Billing is one unit per search TERM and the rows are free, so ask for a large maxItems on few terms rather than the reverse. Use youtube.channel when you already know the handle, youtube.similar\_channels when you have one channel and want others like it, and youtube.videos when you want videos rather than the channels behind them. ## Price **\$0.0030 per event.** That is the rate you pay for each event the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer events costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ---------- | ------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `gl` | `string` | | Two-letter country code, lowercase, passed to YouTube as the gl query parameter. Changes which channels rank for the term, so it is the field to set when you want the creators in one market rather than the global set. The actor uses us when you leave it out. | | `hl` | `string` | | Language code, lowercase, passed to YouTube as the hl query parameter, e.g. en, de, ro. Affects the language of returned descriptions and tags. The actor uses en when you leave it out. | | `sort` | `r`, `ra`, `u`, `v` | | Result ordering: r relevance, ra rating, u upload date, v view count. Defaulted to r and always sent, because the actor returns ZERO results and charges nothing when this field is absent, which is indistinguishable from a topic having no channels. Change it freely, but do not send it empty. Defaults to `"r"`. | | `keywords` | `array` | yes | Search terms, exactly as you would type them into YouTube's search box. Each term is one billed unit and maxItems applies to EACH term, so this array's length is what you pay for. Capped at 10 terms per call. | | `maxItems` | `integer` | | Ceiling on channels returned per term. This does NOT change the price: billing is one event per search term, not per channel returned, so a high value costs exactly what a low one costs. Ask for more rather than less. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"youtube.channel_search","input":{"keywords":["example"]}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "UC7epYXoxGQlErIpBOYoCj9A", "url": "https://www.youtube.com/@AIAutomationLabs", "name": "AI Automation Labs", "tags": [ "AI Automation", "Artificial Intelligence", "AI tools" ], "type": "channel", "handle": "@AIAutomationLabs", "joinedAt": "2023-05-12", "viewCount": "4328162", "thumbnails": [ { "url": "https://yt3.googleusercontent.com/example=s160-c-k-c0x00ffffff-no-rj", "width": 160, "height": 160 } ], "videoCount": "66", "description": "Everything related to A.I !! From How to use A.I tools To Creating crazy A.I Art !!", "isFamilySafe": true, "subscriberCount": 56800, "availableCountries": [ "US", "GB", "DE", "RO" ] } ``` # YouTube comments Source: https://docs.usegoro.ai/tools/social/youtube-comments Comments on one or more YouTube videos, with author, comment text, vote count, reply count, whether the channel owner wrote it, and how long ago it was posted. Comments on one or more YouTube videos, with author, comment text, vote count, reply count, whether the channel owner wrote it, and how long ago it was posted. Use to read audience reaction under specific videos, mine questions, objections or feature requests, or sample sentiment on a launch. Point it at video URLs, not channels. maxComments is per video, so several URLs multiply the bill. ## Price **\$0.0045 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ------------------- | ------------------------------ | -------- | --------------------------------------------------------------------------------------------------------------- | | `startUrls` | `array` | yes | Direct YouTube video URLs. maxComments applies to EACH url, so two urls at 50 comments bills up to 100 comments | | `maxComments` | `integer` | | Maximum comments per video. Billed per comment Defaults to `10`. | | `sortCommentsBy` | `TOP_COMMENTS`, `NEWEST_FIRST` | | Defaults to `"NEWEST_FIRST"`. | | `oldestCommentDate` | `string` | | Only comments posted on or after this date, e.g. 2026-06-01, or a relative age like "7 days" | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"youtube.comments","input":{"startUrls":["example"],"maxComments":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "cid": "sample-id-000", "type": "comment", "title": "How to extract data from YouTube with YouTube Scraper", "author": "example_user", "comment": "Try YouTube Scraper for FREE at https://apify.it/4cjeX9W", "pageUrl": "https://www.youtube.com/watch?v=xObhZ0Ga7EQ", "videoId": "xObhZ0Ga7EQ", "voteCount": 2, "replyCount": 1, "replyToCid": null, "commentsCount": 14, "hasCreatorHeart": false, "publishedTimeText": "4 years ago (edited)", "authorIsChannelOwner": true } ``` # YouTube similar channels Source: https://docs.usegoro.ai/tools/social/youtube-similar_channels Channels similar to one or more seed channels, ranked by what they actually publish rather than by keyword, each with a 0 to 1 similarity score, subscriber and Channels similar to one or more seed channels, ranked by what they actually publish rather than by keyword, each with a 0 to 1 similarity score, subscriber and lifetime view totals, recent average views split across long-form and Shorts, country, join date and verification status. Use to map a competitive field from one example: find who else serves a creator's audience, build a creator shortlist from a channel that already converts, or find the equivalent creator in another country. Seed with CHANNEL IDs in UC form, not handles, and get them from youtube.channel or youtube.channel\_search, both of which return the id you need. Price is the thing to weigh before calling: \$0.36 per matched channel, far above every other row in this catalog, so ask for the number you will actually read. limit is the number you pay for, minimum 10, maximum 25 per call. Filters (subscriber bands, average views, country, verified, has-public-email) narrow the ranking before it is billed, so use them rather than over-fetching and discarding. HasPublicEmail is a true/false flag only, this row never returns an actual email address. ## Price **\$0.36 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ---------------------- | --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `limit` | `integer` | | How many similar channels to return, and EXACTLY what you pay for at $0.36 each. The minimum is 10 rather than 1 because this actor refuses any run whose cost cap is under $2.00, and a smaller request would have to be sent uncapped. The maximum is 25 because a full-size request already holds \$18. | | `countries` | `array` | | Only channels based in these countries, as full names as they appear in the Country output field, e.g. United States. This is the field to use when looking for a creator's equivalent in another market. | | `only_verified` | `boolean` | | Only channels with a YouTube verification badge. Off unless you set it. | | `only_has_email` | `boolean` | | Only channels that expose a public contact email. The address itself is NOT returned, only the HasPublicEmail flag, so this narrows an outreach list rather than building one. | | `max_subscribers` | `integer` | | Only channels with at most this many subscribers. Pair with min\_subscribers to target a size band, e.g. micro-creators. | | `max_total_views` | `integer` | | Only channels with at most this many lifetime views. | | `min_subscribers` | `integer` | | Only channels with at least this many subscribers. Applied before billing. | | `min_total_views` | `integer` | | Only channels with at least this many lifetime views. | | `max_total_videos` | `integer` | | Only channels that have published at most this many videos. | | `min_total_videos` | `integer` | | Only channels that have published at least this many videos. Useful for excluding dormant or one-hit channels. | | `seed_channel_ids` | `array` | yes | YouTube channel IDs in UC form, e.g. UCX6OQ3DkcsbYNE6H8uQQuVA. Handles do NOT resolve here; take the id from youtube.channel or youtube.channel\_search. Seeding several channels finds the intersection of what they have in common, which narrows a niche rather than widening it. Seeds are free, only returned matches are billed. | | `exclude_countries` | `array` | | Drop channels based in these countries. | | `exclude_channel_ids` | `array` | | Channel IDs to leave out of the results, in UC form. Use it to page through a niche without paying for the same channels twice: pass everything you already have. | | `max_avg_views_shorts` | `integer` | | Only channels whose recent Shorts average at most this many views. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"youtube.similar_channels","input":{"seed_channel_ids":["example"],"limit":10}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "Tags": [ "coffee", "espresso", "kitchen gear" ], "Avatar": "https://yt3.googleusercontent.com/example=s800-c-k-c0x00ffffff-no-rj", "Country": "United States", "JoinDate": "2019-03-14", "ChannelId": "UCjq4vqhZQm0Wq6bY5Zk3xPA", "ChannelUrl": "https://www.youtube.com/@counterculture", "FusedScore": 0.8214954206761725, "IsVerified": true, "TotalViews": 58204113, "ChannelName": "Counter Culture", "Description": "Coffee gear tested properly. New teardown every Thursday.", "ChannelHandle": "@counterculture", "HasPublicEmail": true, "SubscriberCount": 412000, "IsFamilyFriendly": true, "TotalVideosCount": 264, "AvgViewsRecentShorts": 421900, "AvgViewsRecentVideos": 184300 } ``` # YouTube transcript Source: https://docs.usegoro.ai/tools/social/youtube-transcript Full transcript of a YouTube video as timestamped lines and optionally as one plain text block, alongside the video's title, channel, duration, view and like co Full transcript of a YouTube video as timestamped lines and optionally as one plain text block, alongside the video's title, channel, duration, view and like counts. Can also walk a channel and transcribe its recent videos. Use when you need what was actually said in a video: summarising, quoting, fact checking, or turning talks and tutorials into text. Give exactly one of youtube\_url or channel\_url. Each transcribed video is billed as one result. ## Price **\$0.0150 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ------------------------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------ | | `end_date` | `string` | | Only channel videos uploaded on or before this date (YYYY-MM-DD). channel\_url only | | `language` | `string` | | Transcript language as ISO 639-1, e.g. en or fr. Leave blank for the video's own default | | `max_videos` | `integer` | | Maximum videos to transcribe from channel\_url. Ignored for youtube\_url. Billed per video Defaults to `10`. | | `start_date` | `string` | | Only channel videos uploaded on or after this date (YYYY-MM-DD). channel\_url only | | `channel_url` | `string` | | A YouTube channel URL. Transcribes up to max\_videos of its videos, each billed as its own result | | `youtube_url` | `string` | | A single YouTube video URL (watch, youtu.be or shorts). Give this OR channel\_url, never both | | `include_transcript_text` | `boolean` | | Also return the whole transcript as one plain string in transcript\_text Defaults to `false`. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"youtube.transcript","input":{"max_videos":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "url": "https://www.youtube.com/watch?v=xObhZ0Ga7EQ", "title": "How to extract data from YouTube with YouTube Scraper", "status": "success", "message": "Successfully fetched the transcript for the video with ID 'xObhZ0Ga7EQ'", "language": "en", "video_id": "xObhZ0Ga7EQ", "channel_id": "UCTgwcoeGGKmZ3zzCXN2qo_A", "like_count": 78, "transcript": [ { "end": 5.44, "text": "Hi, welcome to Apify, your one-stop-shop for web scraping and automation.", "start": 0.149, "duration": 5.291 } ], "view_count": 10035, "channel_name": "Apify", "published_at": "2022-03-15T10:46:41Z", "comment_count": 14, "transcript_text": "Hi, welcome to Apify, your one-stop-shop for web scraping and automation.", "channel_username": "apify", "duration_seconds": 175, "subscriber_count": 16200, "is_auto_generated": false, "selected_language": "English", "available_languages": [ "English", "English (auto-generated)" ] } ``` # YouTube trending Source: https://docs.usegoro.ai/tools/social/youtube-trending The videos on YouTube's trending shelf for one country right now, in rank order, each with view, like and comment counts, duration, category, publish date and t The videos on YouTube's trending shelf for one country right now, in rank order, each with view, like and comment counts, duration, category, publish date and the channel behind it. Use when the question is what YouTube is pushing RIGHT NOW rather than what matches a search term: spotting a format or topic while it is surfacing, checking whether a niche is having a moment in one market, or sampling what the algorithm currently rewards. This is the trending shelf itself, which is not the same set as a search: it is where a format shows up before anyone searches for it by name. Use youtube.videos for search terms and channel catalogues, trends.search for Google search demand (it cannot be scoped to YouTube), and tiktok.api for the equivalent on TikTok. One country per call, since gl takes a single code, so a five-market sweep is five calls. Price is FLAT per call: 100 videos cost exactly what 5 cost, so always ask for more. Be careful piping this straight into a context window, rows carry full streaming and caption metadata and run roughly 38 KB each. ## Price **\$0.0750 per event.** That is the rate you pay for each event the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer events costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ---------- | --------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `gl` | `string` | | Country whose trending shelf to read, as a lowercase two-letter code, e.g. us, gb, de, ro. One country per call. The actor uses us when you leave it out. | | `maxItems` | `integer` | | How many trending videos to return. This does NOT change the price: the call bills one flat query event whatever the count, verified at 5 and at 100 videos for the identical \$0.025. Ask for as many as you can use. Every row is large, so the practical ceiling is what you can afford to read, not what you can afford to buy. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"youtube.trending","input":{}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "dQw4w9WgXcQ", "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "type": "video", "likes": 84120, "title": "I Tested Every Budget Espresso Machine", "views": 1284093, "isLive": false, "channel": { "id": "UCjq4vqhZQm0Wq6bY5Zk3xPA", "url": "https://www.youtube.com/channel/UCjq4vqhZQm0Wq6bY5Zk3xPA", "name": "Counter Culture", "handle": "@counterculture", "subscriberCount": "412K subscribers" }, "category": "Howto & Style", "comments": 5310, "duration": 743, "keywords": [ "espresso", "coffee gear", "kitchen" ], "isPrivate": false, "isUnlisted": false, "thumbnails": [ { "url": "https://i.ytimg.com/vi/example/maxresdefault.jpg", "width": 1280, "height": 720 } ], "uploadDate": "2026-08-06T14:02:11-07:00", "description": "Fifteen machines, one kitchen, and a lot of ruined shots.", "publishDate": "2026-08-06T14:02:11-07:00", "isFamilySafe": true, "relativeDate": "2026-08-06T14:02:11-07:00" } ``` # YouTube videos Source: https://docs.usegoro.ai/tools/social/youtube-videos YouTube videos from a search term, channel, playlist or direct URL, with title, channel, view and like counts, duration, upload date, description and hashtags. YouTube videos from a search term, channel, playlist or direct URL, with title, channel, view and like counts, duration, upload date, description and hashtags. Use to find videos on a topic, pull a channel's or playlist's catalogue, or enrich known video URLs with their stats. Subtitles and the two AI add-ons are billed extra per video, so leave them off unless you need them. ## Price **\$0.0090 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ------------- | ---------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ | | `is3D` | `boolean` | | searchQueries filter: 3D videos only. | | `is4K` | `boolean` | | searchQueries filter: 4K videos only. | | `isHD` | `boolean` | | searchQueries filter: HD videos only. | | `hasCC` | `boolean` | | searchQueries filter: Creative Commons licensed videos only. | | `is360` | `boolean` | | searchQueries filter: 360 degree videos only. | | `isHDR` | `boolean` | | searchQueries filter: HDR videos only. | | `isLive` | `boolean` | | searchQueries filter: live videos only. | | `isVR180` | `boolean` | | searchQueries filter: VR180 videos only. | | `isBought` | `boolean` | | searchQueries filter: purchased videos only. | | `startUrls` | `array` | | Direct YouTube video, channel, playlist, hashtag or results-page URLs | | `videoType` | `video`, `movie` | | | | `dateFilter` | `hour`, `today`, `week`, `month`, `year` | | Upload-date filter for search. COSTS EXTRA on channel inputs: the date-range add-on is billed \$0.001 per video on top of the result | | `maxResults` | `integer` | | Maximum regular videos per search term or channel. Billed per video Defaults to `10`. | | `hasLocation` | `boolean` | | searchQueries filter: videos tagged with a location. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"youtube.videos","input":{"maxResults":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "id": "5kcaHAuGxmY", "url": "https://www.youtube.com/watch?v=5kcaHAuGxmY", "date": "2024-08-21T17:50:32.000Z", "text": "Scrape data from any website with this Web Scraper API from Apify Store.", "type": "video", "input": "apify web scraping", "likes": 343, "title": "How to scrape ANY website with Apify's Web Scraper API", "duration": "00:05:30", "hashtags": [ "#webscraping", "#coding" ], "channelId": "UCTgwcoeGGKmZ3zzCXN2qo_A", "subtitles": null, "viewCount": 40106, "channelUrl": "https://www.youtube.com/@Apify", "channelName": "Apify", "thumbnailUrl": "https://i.ytimg.com/vi/5kcaHAuGxmY/maxresdefault.jpg", "commentsCount": 44, "aiVideoSummary": null, "channelUsername": "Apify", "aiVideoDescription": null, "numberOfSubscribers": 16200 } ``` # Gemini Omni 1.1 Flash video Source: https://docs.usegoro.ai/tools/video/video-gemini_omni Generate a 4, 6, 8 or 10 second video at 1080p from a text prompt using Google Gemini Omni 1.1 Flash, or animate a still image you supply as the first frame. Pr Generate a 4, 6, 8 or 10 second video at 1080p from a text prompt using Google Gemini Omni 1.1 Flash, or animate a still image you supply as the first frame. Priced per video by its length. Returns the URL of the generated MP4. That URL is temporary: Goro holds a run's results for 24 hours and nothing guarantees the file outlives that, so collect what you need within 24 hours. Use when you want Google's video model with a fixed, known price per clip rather than a per-second meter: explainer shots, product reveals, short social clips where the brief fits in ten seconds. Strong prompt adherence and clean 1080p output. To animate a local or private image, upload it first (POST /v1/uploads, or create\_upload over MCP, or goro run --image) and pass the handle as image\_handle. The price is exact once you pick the duration. ## Price **$1.05 per call at `8`.** A flat price, and the only thing that changes it is `duration`: `4` costs **$0.63**, `6` costs **$0.84**, `8` costs **$1.05**, `10` costs **\$1.26**. Every one of those is exact and known before the call, so the hold and the charge are the same number and there is no ceiling to reconcile. Omit `duration` and you are billed the `8` price. A call that fails, times out or is stopped releases its hold in full and costs nothing. ## Input | Field | Type | Required | Notes | | -------------- | ------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `prompt` | `string` | yes | What to film. Describe the visual content, style, cinematography and any character actions. | | `duration` | `4`, `6`, `8`, `10` | | Length of the generated video in seconds, one of 4, 6, 8 or 10. This is the only field that changes the price: $0.63, $0.84, $1.05 or $1.26 per video. Defaults to `8`. | | `image_urls` | `array` | | Optional. One public http or https link to the image the video should start from (JPEG, PNG or WEBP, up to 10 MB). Use this when the image is ALREADY published somewhere; the output URL of an image endpoint works directly. Nothing is copied or re-hosted, we pass the link on. For a file that is not already public, use image\_handle instead and it never has to be published anywhere. Send one of the three, never two. | | `resolution` | `1080p` | | Fixed at 1080p, which costs the same as 360p and 720p on this model; 4k is a different price and would get its own endpoint. Defaults to `"1080p"`. | | `aspect_ratio` | `16:9`, `9:16` | | Shape of the output video. 16:9 for landscape, 9:16 for vertical. Defaults to `"16:9"`. | | `image_base64` | `string` | | Optional, and only for a SMALL image (under 3 MB) or a caller that cannot run a shell command. The image inline, as base64 of the file's own bytes; a data:image/png;base64, prefix is accepted and its declared type ignored. Understand the cost before choosing it: this string is part of the request, and on an MCP call the request IS the tool arguments, which pass through the model's context window. A 3 MB photograph is 4 MB of base64, on the order of a million tokens, and in practice it is truncated and the run fails looking like file corruption. Do not downscale an image to fit here: use image\_handle, which takes up to 10 MB and keeps the file out of the conversation entirely. The 3 MB ceiling is 4,194,304 base64 characters and is a property of the request body, not of the model. JPEG, PNG or WEBP, checked against the file's own leading bytes rather than against any type you declare. Send one of the three, never two. | | `image_handle` | `string` | | Optional, and the RIGHT WAY to send a local or private image. Upload the file first with POST /v1/uploads (or the create\_upload MCP tool, or the CLI's --image flag, which does the whole thing for you) and put the short handle it returns here. The file goes straight from you to a private bucket: it never passes through a tool call, so it never passes through a model's context window, and up to 10 MB is fine. Nothing is published. The handle is usable for 10 minutes after the upload lands and the file is deleted 30 minutes after that. Supplying it makes this an image-to-video call, at exactly the same price. Send one of image\_handle, image\_urls and image\_base64, never two. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"video.gemini_omni","input":{"prompt":"A hummingbird hovering at a red flower, macro, morning light, slow motion."}}' ``` ## Response The full response. Values are illustrative. ```json theme={null} [ "https://example.com/goro/generated-video-1.mp4" ] ``` # Grok Imagine Video 1.5 Source: https://docs.usegoro.ai/tools/video/video-grok Generate a video from a text prompt using xAI Grok Imagine Video 1.5 at 720p with synchronized audio, or animate a still image you supply. Clips run 1 to 15 sec Generate a video from a text prompt using xAI Grok Imagine Video 1.5 at 720p with synchronized audio, or animate a still image you supply. Clips run 1 to 15 seconds. Returns the URL of the generated MP4. That URL is temporary: Goro holds a run's results for 24 hours and nothing guarantees the file outlives that, so collect what you need within 24 hours. Use when you need motion cheaply or at volume: drafts, moving backgrounds, quick social clips, or ten takes on an idea before committing to an expensive model. The cheapest video model in the catalog, and it animates a photograph at the same rate as it films a prompt. To send a local or private image, upload it first (POST /v1/uploads, or create\_upload over MCP, or goro run --image) and pass the handle as image\_handle. Billed per second of video you ask for. ## Price **\$0.0450 per second of video.** An exact rate, not an estimate. The total is that rate times the seconds of video in your request, so you can work out what a call costs before you make it. No call costs less than $0.0020 or more than $0.6750, the price of the largest request this endpoint takes, 15 seconds of video. `discover` and `inspect` quote your exact request before it runs. ## Input | Field | Type | Required | Notes | | -------------- | ------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `prompt` | `string` | yes | What to film. Describe the subject, the action, the camera move and the timing; sound is generated to match. | | `duration` | `integer` | | Length of the generated video in seconds. This is what you are billed on, and it is the only field that changes the price. Defaults to `8`. | | `image_urls` | `array` | | Optional. One public http or https link to the image the video should start from (JPEG, PNG or WEBP, up to 10 MB). Use this when the image is ALREADY published somewhere; the output URL of an image endpoint works directly. Nothing is copied or re-hosted, we pass the link on. For a file that is not already public, use image\_handle instead and it never has to be published anywhere. Send one of the three, never two. | | `resolution` | `720p` | | Fixed at 720p. Resolution changes the price per second, and this endpoint quotes one exact rate, so 480p would get its own endpoint. Defaults to `"720p"`. | | `aspect_ratio` | `1:1`, `16:9`, `9:16`, `3:2`, `2:3`, `auto` | | Shape of the output video. Ignored when a starting image is supplied, which decides it. Defaults to `"16:9"`. | | `image_base64` | `string` | | Optional, and only for a SMALL image (under 3 MB) or a caller that cannot run a shell command. The image inline, as base64 of the file's own bytes; a data:image/png;base64, prefix is accepted and its declared type ignored. Understand the cost before choosing it: this string is part of the request, and on an MCP call the request IS the tool arguments, which pass through the model's context window. A 3 MB photograph is 4 MB of base64, on the order of a million tokens, and in practice it is truncated and the run fails looking like file corruption. Do not downscale an image to fit here: use image\_handle, which takes up to 10 MB and keeps the file out of the conversation entirely. The 3 MB ceiling is 4,194,304 base64 characters and is a property of the request body, not of the model. JPEG, PNG or WEBP, checked against the file's own leading bytes rather than against any type you declare. Send one of the three, never two. | | `image_handle` | `string` | | Optional, and the RIGHT WAY to send a local or private image. Upload the file first with POST /v1/uploads (or the create\_upload MCP tool, or the CLI's --image flag, which does the whole thing for you) and put the short handle it returns here. The file goes straight from you to a private bucket: it never passes through a tool call, so it never passes through a model's context window, and up to 10 MB is fine. Nothing is published. The handle is usable for 10 minutes after the upload lands and the file is deleted 30 minutes after that. Supplying it makes this an image-to-video call, at exactly the same price. Send one of image\_handle, image\_urls and image\_base64, never two. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"video.grok","input":{"prompt":"A paper boat drifting across a still pond, slow push in."}}' ``` ## Response The full response. Values are illustrative. ```json theme={null} [ "https://example.com/goro/generated-video-1.mp4" ] ``` # Kling 3.0 Omni video Source: https://docs.usegoro.ai/tools/video/video-kling Animate a still image you supply into a 720p video with native audio using Kling 3.0 Omni: the picture is the first frame, the prompt says what happens next. Cl Animate a still image you supply into a 720p video with native audio using Kling 3.0 Omni: the picture is the first frame, the prompt says what happens next. Clips run 3 to 15 seconds. Returns the URL of the generated MP4. That URL is temporary: Goro holds a run's results for 24 hours and nothing guarantees the file outlives that, so collect what you need within 24 hours. Use when you have the exact frame the video must start from (a product shot, a character, a location) and want it brought to life with sound: an ad still that needs motion, a portrait that should turn and speak, a mockup that should be handled. Better at keeping the starting image intact than the cheaper models. To send a local or private image, upload it first (POST /v1/uploads, or create\_upload over MCP, or goro run --image) and pass the handle as image\_handle. Billed per second of video you ask for. ## Price **\$0.18 per second of video.** An exact rate, not an estimate. The total is that rate times the seconds of video in your request, so you can work out what a call costs before you make it. No call costs less than $0.0020 or more than $2.70, the price of the largest request this endpoint takes, 15 seconds of video. `discover` and `inspect` quote your exact request before it runs. ## Input | Field | Type | Required | Notes | | -------------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `audio` | `true` | | Fixed on. Native audio is part of what this endpoint quotes; a silent video would be a different price. Defaults to `true`. | | `prompt` | `string` | yes | How the image should move. Describe the action, the camera move and any dialogue or sound; Kling generates matching audio. | | `duration` | `integer` | | Length of the generated video in seconds. This is what you are billed on, and it is the only field that changes the price. Defaults to `5`. | | `image_urls` | `array` | | One public http or https link to the starting image (JPEG, PNG or WEBP, up to 10 MB). Use this when the image is ALREADY published somewhere; the output URL of an image endpoint works directly. For a file that is not already public, send image\_handle instead and it never has to be published anywhere. Exactly one of image\_handle, image\_urls and image\_base64 is required. | | `resolution` | `720p` | | Fixed at 720p. Resolution changes the price per second, and this endpoint quotes one exact rate, so 1080p and 4k would get their own endpoints. Defaults to `"720p"`. | | `image_base64` | `string` | | Only for a SMALL image (under 3 MB) or a caller that cannot run a shell command. The image inline, as base64 of the file's own bytes; a data:image/png;base64, prefix is accepted and its declared type ignored. Understand the cost before choosing it: this string is part of the request, and on an MCP call the request IS the tool arguments, which pass through the model's context window. A 3 MB photograph is 4 MB of base64, on the order of a million tokens, and in practice it is truncated and the run fails looking like file corruption. Do not downscale an image to fit here: use image\_handle, which takes up to 10 MB and keeps the file out of the conversation entirely. JPEG, PNG or WEBP. Exactly one of image\_handle, image\_urls and image\_base64 is required. | | `image_handle` | `string` | | The RIGHT WAY to send a local or private image. Upload the file first with POST /v1/uploads (or the create\_upload MCP tool, or the CLI's --image flag, which does the whole thing for you) and put the short handle it returns here. The file goes straight from you to a private bucket: it never passes through a tool call, so it never passes through a model's context window, and up to 10 MB is fine. Nothing is published. The handle is usable for 10 minutes after the upload lands and the file is deleted 30 minutes after that. JPEG, PNG or WEBP. Exactly one of image\_handle, image\_urls and image\_base64 is required. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"video.kling","input":{"prompt":"The woman turns to the window and smiles as the curtain lifts in a breeze."}}' ``` ## Response The full response. Values are illustrative. ```json theme={null} [ "https://example.com/goro/generated-video-1.mp4" ] ``` # MiniMax H3 video Source: https://docs.usegoro.ai/tools/video/video-minimax Generate a video from a text prompt using MiniMax H3 (Hailuo 03) at 2K with native audio, or animate a still image you supply. Returns the URL of the generated Generate a video from a text prompt using MiniMax H3 (Hailuo 03) at 2K with native audio, or animate a still image you supply. Returns the URL of the generated MP4. That URL is temporary: Goro holds a run's results for 24 hours and nothing guarantees the file outlives that, so collect what you need within 24 hours. Use when you want the highest resolution in the catalog, with stereo sound and strong motion, from a text prompt or from one starting image. A step up from seedance and grok, a step down in price from Veo. An image costs the same per second as a text prompt here. To send a local or private image, upload it first (POST /v1/uploads, or create\_upload over MCP, or goro run --image) and pass the handle as image\_handle, so the file never travels through the request. Billed per second of video you ask for. ## Price **\$0.1950 per second of video.** An exact rate, not an estimate. The total is that rate times the seconds of video in your request, so you can work out what a call costs before you make it. No call costs less than $0.0020 or more than $2.9250, the price of the largest request this endpoint takes, 15 seconds of video. `discover` and `inspect` quote your exact request before it runs. ## Input | Field | Type | Required | Notes | | -------------- | ------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `prompt` | `string` | yes | What to film. Describe the subject, the action, the camera move, the lighting and the mood. | | `duration` | `integer` | | Length of the generated video in seconds. This is what you are billed on, and it is the only field that changes the price. Defaults to `6`. | | `image_urls` | `array` | | Optional. One public http or https link to the image the video should start from (JPEG, PNG or WEBP, up to 10 MB). Use this when the image is ALREADY published somewhere; the output URL of an image endpoint works directly. Nothing is copied or re-hosted, we pass the link on. For a file that is not already public, use image\_handle instead and it never has to be published anywhere. Send one of the three, never two. | | `aspect_ratio` | `21:9`, `16:9`, `4:3`, `1:1`, `3:4`, `9:16` | | Shape of the output video. Defaults to `"16:9"`. | | `image_base64` | `string` | | Optional, and only for a SMALL image (under 3 MB) or a caller that cannot run a shell command. The image inline, as base64 of the file's own bytes; a data:image/png;base64, prefix is accepted and its declared type ignored. Understand the cost before choosing it: this string is part of the request, and on an MCP call the request IS the tool arguments, which pass through the model's context window. A 3 MB photograph is 4 MB of base64, on the order of a million tokens, and in practice it is truncated and the run fails looking like file corruption. Do not downscale an image to fit here: use image\_handle, which takes up to 10 MB and keeps the file out of the conversation entirely. The 3 MB ceiling is 4,194,304 base64 characters and is a property of the request body, not of the model. JPEG, PNG or WEBP, checked against the file's own leading bytes rather than against any type you declare. Send one of the three, never two. | | `image_handle` | `string` | | Optional, and the RIGHT WAY to send a local or private image. Upload the file first with POST /v1/uploads (or the create\_upload MCP tool, or the CLI's --image flag, which does the whole thing for you) and put the short handle it returns here. The file goes straight from you to a private bucket: it never passes through a tool call, so it never passes through a model's context window, and up to 10 MB is fine. Nothing is published. The handle is usable for 10 minutes after the upload lands and the file is deleted 30 minutes after that. Supplying it makes this an image-to-video call, at exactly the same price per second. Send one of image\_handle, image\_urls and image\_base64, never two. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"video.minimax","input":{"prompt":"A cat walking slowly along a beach at sunset, cinematic tracking shot, waves lapping the shore."}}' ``` ## Response The full response. Values are illustrative. ```json theme={null} [ "https://example.com/goro/generated-video-1.mp4" ] ``` # PixVerse V6 video Source: https://docs.usegoro.ai/tools/video/video-pixverse Generate a video from a text prompt using PixVerse V6 at 720p with synchronized audio, or animate a still image you supply. Clips run 1 to 15 seconds. Returns t Generate a video from a text prompt using PixVerse V6 at 720p with synchronized audio, or animate a still image you supply. Clips run 1 to 15 seconds. Returns the URL of the generated MP4. That URL is temporary: Goro holds a run's results for 24 hours and nothing guarantees the file outlives that, so collect what you need within 24 hours. Use for stylised, cinematic short clips at a low price with sound included: social hooks, transitions, mood pieces, anime and illustration styles. Sits between Grok and the premium models on price. To animate a local or private image, upload it first (POST /v1/uploads, or create\_upload over MCP, or goro run --image) and pass the handle as image\_handle. Billed per second of video you ask for. ## Price **\$0.0960 per second of video.** An exact rate, not an estimate. The total is that rate times the seconds of video in your request, so you can work out what a call costs before you make it. No call costs less than $0.0020 or more than $1.44, the price of the largest request this endpoint takes, 15 seconds of video. `discover` and `inspect` quote your exact request before it runs. ## Input | Field | Type | Required | Notes | | ----------------------- | --------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `prompt` | `string` | yes | What to film. Describe the subject, the action, the camera move and the style. | | `quality` | `720p` | | Fixed at 720p (this model calls its resolution quality). Resolution changes the price per second, and this endpoint quotes one exact rate, so the other tiers would get their own endpoints. Defaults to `"720p"`. | | `duration` | `integer` | | Length of the generated video in seconds. This is what you are billed on, and it is the only field that changes the price. Defaults to `5`. | | `image_urls` | `array` | | Optional. One public http or https link to the image the video should start from (JPEG, PNG or WEBP, up to 10 MB). Use this when the image is ALREADY published somewhere; the output URL of an image endpoint works directly. Nothing is copied or re-hosted, we pass the link on. For a file that is not already public, use image\_handle instead and it never has to be published anywhere. Send one of the three, never two. | | `aspect_ratio` | `16:9`, `4:3`, `1:1`, `3:4`, `9:16`, `2:3`, `3:2`, `21:9` | | Shape of the output video. Ignored when a starting image is supplied, which decides it. Defaults to `"16:9"`. | | `image_base64` | `string` | | Optional, and only for a SMALL image (under 3 MB) or a caller that cannot run a shell command. The image inline, as base64 of the file's own bytes; a data:image/png;base64, prefix is accepted and its declared type ignored. Understand the cost before choosing it: this string is part of the request, and on an MCP call the request IS the tool arguments, which pass through the model's context window. A 3 MB photograph is 4 MB of base64, on the order of a million tokens, and in practice it is truncated and the run fails looking like file corruption. Do not downscale an image to fit here: use image\_handle, which takes up to 10 MB and keeps the file out of the conversation entirely. The 3 MB ceiling is 4,194,304 base64 characters and is a property of the request body, not of the model. JPEG, PNG or WEBP, checked against the file's own leading bytes rather than against any type you declare. Send one of the three, never two. | | `image_handle` | `string` | | Optional, and the RIGHT WAY to send a local or private image. Upload the file first with POST /v1/uploads (or the create\_upload MCP tool, or the CLI's --image flag, which does the whole thing for you) and put the short handle it returns here. The file goes straight from you to a private bucket: it never passes through a tool call, so it never passes through a model's context window, and up to 10 MB is fine. Nothing is published. The handle is usable for 10 minutes after the upload lands and the file is deleted 30 minutes after that. Supplying it makes this an image-to-video call, at exactly the same price. Send one of image\_handle, image\_urls and image\_base64, never two. | | `generate_audio_switch` | `true` | | Fixed on. Audio is part of what this endpoint quotes; a silent video would be a different price. Defaults to `true`. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"video.pixverse","input":{"prompt":"An astronaut walking through a field of sunflowers, wide shot, painterly."}}' ``` ## Response The full response. Values are illustrative. ```json theme={null} [ "https://example.com/goro/generated-video-1.mp4" ] ``` # Seedance 2.0 Mini video Source: https://docs.usegoro.ai/tools/video/video-seedance Generate a video from a text prompt using ByteDance Seedance 2.0 Mini, at 720p with optional audio, or animate a still image you supply. Returns the URL of the Generate a video from a text prompt using ByteDance Seedance 2.0 Mini, at 720p with optional audio, or animate a still image you supply. Returns the URL of the generated MP4. That URL is temporary: Goro holds a run's results for 24 hours and nothing guarantees the file outlives that, so collect what you need within 24 hours. Use as the sensible default for social video and B-roll: cinematic motion and consistent characters at a fraction of the flagship models, from a prompt or from a starting image at the same price per second. Reach for video.seedance\_pro when the same shot needs to be sharper. To send a local or private image, upload it first (POST /v1/uploads, or create\_upload over MCP, or goro run --image) and pass the handle as image\_handle, so the file never travels through the request. Billed per second of video you ask for. ## Price **\$0.2050 per second of video.** An exact rate, not an estimate. The total is that rate times the seconds of video in your request, so you can work out what a call costs before you make it. No call costs less than $0.0020 or more than $3.0750, the price of the largest request this endpoint takes, 15 seconds of video. `discover` and `inspect` quote your exact request before it runs. ## Input | Field | Type | Required | Notes | | ---------------- | ------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `prompt` | `string` | yes | What to film. Describe the subject, the action, the camera move, the lighting and the mood; long, shot-by-shot briefs work well here. | | `duration` | `integer` | | Length of the generated video in seconds. This is what you are billed on, and it is the only field that changes the price. Defaults to `5`. | | `image_urls` | `array` | | Optional. One public http or https link to the image the video should start from (JPEG, PNG or WEBP, up to 10 MB). Use this when the image is ALREADY published somewhere; the output URL of an image endpoint works directly. Nothing is copied or re-hosted, we pass the link on. For a file that is not already public, use image\_handle instead and it never has to be published anywhere. Send one of the three, never two. | | `resolution` | `720p` | | Fixed at 720p. Resolution changes the price per second, and this endpoint quotes one exact rate, so 480p would get its own endpoint. Defaults to `"720p"`. | | `aspect_ratio` | `1:1`, `4:3`, `3:4`, `16:9`, `9:16`, `21:9` | | Shape of the output video. Defaults to `"16:9"`. | | `image_base64` | `string` | | Optional, and only for a SMALL image (under 3 MB) or a caller that cannot run a shell command. The image inline, as base64 of the file's own bytes; a data:image/png;base64, prefix is accepted and its declared type ignored. Understand the cost before choosing it: this string is part of the request, and on an MCP call the request IS the tool arguments, which pass through the model's context window. A 3 MB photograph is 4 MB of base64, on the order of a million tokens, and in practice it is truncated and the run fails looking like file corruption. Do not downscale an image to fit here: use image\_handle, which takes up to 10 MB and keeps the file out of the conversation entirely. The 3 MB ceiling is 4,194,304 base64 characters and is a property of the request body, not of the model. JPEG, PNG or WEBP, checked against the file's own leading bytes rather than against any type you declare. Send one of the three, never two. | | `image_handle` | `string` | | Optional, and the RIGHT WAY to send a local or private image. Upload the file first with POST /v1/uploads (or the create\_upload MCP tool, or the CLI's --image flag, which does the whole thing for you) and put the short handle it returns here. The file goes straight from you to a private bucket: it never passes through a tool call, so it never passes through a model's context window, and up to 10 MB is fine. Nothing is published. The handle is usable for 10 minutes after the upload lands and the file is deleted 30 minutes after that. Supplying it makes this an image-to-video call, at exactly the same price per second. Send one of image\_handle, image\_urls and image\_base64, never two. | | `generate_audio` | `boolean` | | Whether the video comes with a generated soundtrack. Does not change the price. Defaults to `true`. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"video.seedance","input":{"prompt":"A serene beach at sunset, waves gently crashing, palm trees swaying, seagulls crossing an orange sky."}}' ``` ## Response The full response. Values are illustrative. ```json theme={null} [ "https://example.com/goro/generated-video-1.mp4" ] ``` # Seedance 2.5 video Source: https://docs.usegoro.ai/tools/video/video-seedance_pro Generate a video from a text prompt using ByteDance Seedance 2.5 at 720p with optional audio, or animate a still image you supply. Clips run 4 to 30 seconds, th Generate a video from a text prompt using ByteDance Seedance 2.5 at 720p with optional audio, or animate a still image you supply. Clips run 4 to 30 seconds, the longest of the premium models. Returns the URL of the generated MP4. That URL is temporary: Goro holds a run's results for 24 hours and nothing guarantees the file outlives that, so collect what you need within 24 hours. Use when Seedance Mini is close but not sharp enough, or when you need one continuous shot longer than fifteen seconds: the same prompt handling and multi-shot consistency with noticeably better detail and motion, from a prompt or from a starting image at the same price per second. To send a local or private image, upload it first (POST /v1/uploads, or create\_upload over MCP, or goro run --image) and pass the handle as image\_handle. Billed per second of video you ask for. ## Price **\$0.63 per second of video.** An exact rate, not an estimate. The total is that rate times the seconds of video in your request, so you can work out what a call costs before you make it. No call costs less than $0.0020 or more than $18.90, the price of the largest request this endpoint takes, 30 seconds of video. `discover` and `inspect` quote your exact request before it runs. ## Input | Field | Type | Required | Notes | | ---------------- | ------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `prompt` | `string` | yes | What to film. Describe the subject, the action, the camera move, the lighting and the mood; long, shot-by-shot briefs work well here. | | `duration` | `integer` | | Length of the generated video in seconds. This is what you are billed on, and it is the only field that changes the price. Defaults to `5`. | | `image_urls` | `array` | | Optional. One public http or https link to the image the video should start from (JPEG, PNG or WEBP, up to 10 MB). Use this when the image is ALREADY published somewhere; the output URL of an image endpoint works directly. Nothing is copied or re-hosted, we pass the link on. For a file that is not already public, use image\_handle instead and it never has to be published anywhere. Send one of the three, never two. | | `resolution` | `720p` | | Fixed at 720p. Resolution changes the price per second, and this endpoint quotes one exact rate, so 480p and 1080p would get their own endpoints. Defaults to `"720p"`. | | `aspect_ratio` | `1:1`, `4:3`, `3:4`, `16:9`, `9:16`, `21:9`, `adaptive` | | Shape of the output video. adaptive follows the starting image when there is one. Defaults to `"16:9"`. | | `image_base64` | `string` | | Optional, and only for a SMALL image (under 3 MB) or a caller that cannot run a shell command. The image inline, as base64 of the file's own bytes; a data:image/png;base64, prefix is accepted and its declared type ignored. Understand the cost before choosing it: this string is part of the request, and on an MCP call the request IS the tool arguments, which pass through the model's context window. A 3 MB photograph is 4 MB of base64, on the order of a million tokens, and in practice it is truncated and the run fails looking like file corruption. Do not downscale an image to fit here: use image\_handle, which takes up to 10 MB and keeps the file out of the conversation entirely. The 3 MB ceiling is 4,194,304 base64 characters and is a property of the request body, not of the model. JPEG, PNG or WEBP, checked against the file's own leading bytes rather than against any type you declare. Send one of the three, never two. | | `image_handle` | `string` | | Optional, and the RIGHT WAY to send a local or private image. Upload the file first with POST /v1/uploads (or the create\_upload MCP tool, or the CLI's --image flag, which does the whole thing for you) and put the short handle it returns here. The file goes straight from you to a private bucket: it never passes through a tool call, so it never passes through a model's context window, and up to 10 MB is fine. Nothing is published. The handle is usable for 10 minutes after the upload lands and the file is deleted 30 minutes after that. Supplying it makes this an image-to-video call, at exactly the same price. Send one of image\_handle, image\_urls and image\_base64, never two. | | `generate_audio` | `boolean` | | Whether the video comes with a generated soundtrack. Defaults to `true`. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"video.seedance_pro","input":{"prompt":"A chef plating a dish in a warm kitchen, slow dolly around the pass, steam catching the window light."}}' ``` ## Response The full response. Values are illustrative. ```json theme={null} [ "https://example.com/goro/generated-video-1.mp4" ] ``` # Veo 3.1 video Source: https://docs.usegoro.ai/tools/video/video-veo Generate an 8 second video with sound using Google Veo 3.1 at its Quality tier in 1080p, from a text prompt or from a still image you supply. Returns the URL of Generate an 8 second video with sound using Google Veo 3.1 at its Quality tier in 1080p, from a text prompt or from a still image you supply. Returns the URL of the generated MP4. That URL is temporary: Goro holds a run's results for 24 hours and nothing guarantees the file outlives that, so collect what you need within 24 hours. Use when the video itself is the deliverable and it has to hold up: an ad, a hero clip, a pitch asset. The strongest model in the catalog for prompt following, physical realism and synchronised audio, and the most expensive by a wide margin. Takes a text prompt or one starting image, at one flat price per video either way, so reach for video.grok first if you are only exploring. To send a local or private image, upload it first (POST /v1/uploads, or create\_upload over MCP, or goro run --image) and pass the handle as image\_handle, so the file never travels through the request. ## Price **\$2.55 per call.** A flat price. Every call that succeeds costs exactly this, whatever it returns, and you know the number before you make the call. There is no ceiling to read and no usage to reconcile afterwards. A call that fails, times out or is stopped releases its hold in full and costs nothing. ## Input | Field | Type | Required | Notes | | -------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `prompt` | `string` | yes | What to film. Describe the subject, the action, the camera move, the lighting and the mood; dialogue and sound effects can be described too and will be generated as audio. | | `duration` | `8` | | Fixed at 8 seconds. This model is priced per video rather than per second, so a shorter clip would cost you the same. Defaults to `8`. | | `image_urls` | `array` | | Optional. One public http or https link to the image the video should start from (JPEG, PNG or WEBP, up to 10 MB). Use this when the image is ALREADY published somewhere; the output URL of an image endpoint works directly. Nothing is copied or re-hosted, we pass the link on. For a file that is not already public, use image\_handle instead and it never has to be published anywhere. Send one of the three, never two. | | `resolution` | `1080p` | | Fixed at 1080p. Each resolution is a different price upstream, and this endpoint quotes one exact price, so the others would get their own endpoints. Defaults to `"1080p"`. | | `aspect_ratio` | `16:9`, `9:16` | | Shape of the output video. 16:9 for landscape, 9:16 for vertical. Defaults to `"16:9"`. | | `image_base64` | `string` | | Optional, and only for a SMALL image (under 3 MB) or a caller that cannot run a shell command. The image inline, as base64 of the file's own bytes; a data:image/png;base64, prefix is accepted and its declared type ignored. Understand the cost before choosing it: this string is part of the request, and on an MCP call the request IS the tool arguments, which pass through the model's context window. A 3 MB photograph is 4 MB of base64, on the order of a million tokens, and in practice it is truncated and the run fails looking like file corruption. Do not downscale an image to fit here: use image\_handle, which takes up to 10 MB and keeps the file out of the conversation entirely. The 3 MB ceiling is 4,194,304 base64 characters and is a property of the request body, not of the model. JPEG, PNG or WEBP, checked against the file's own leading bytes rather than against any type you declare. Send one of the three, never two. | | `image_handle` | `string` | | Optional, and the RIGHT WAY to send a local or private image. Upload the file first with POST /v1/uploads (or the create\_upload MCP tool, or the CLI's --image flag, which does the whole thing for you) and put the short handle it returns here. The file goes straight from you to a private bucket: it never passes through a tool call, so it never passes through a model's context window, and up to 10 MB is fine. Nothing is published. The handle is usable for 10 minutes after the upload lands and the file is deleted 30 minutes after that. Supplying it makes this an image-to-video call, at exactly the same price per second. Send one of image\_handle, image\_urls and image\_base64, never two. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"video.veo","input":{"prompt":"A paper boat drifting across a still pond at dawn, slow push in, mist on the water, soft birdsong."}}' ``` ## Response The full response. Values are illustrative. ```json theme={null} [ "https://example.com/goro/generated-video-1.mp4" ] ``` # Wan 3.0 video Source: https://docs.usegoro.ai/tools/video/video-wan Generate a video from a text prompt using Alibaba Wan 3.0 at 720P with audio, or animate a still image you supply as the first frame. Clips run 2 to 30 seconds. Generate a video from a text prompt using Alibaba Wan 3.0 at 720P with audio, or animate a still image you supply as the first frame. Clips run 2 to 30 seconds. Returns the URL of the generated MP4. That URL is temporary: Goro holds a run's results for 24 hours and nothing guarantees the file outlives that, so collect what you need within 24 hours. Use for realistic, controllable motion at a mid-range price, especially for longer continuous shots up to thirty seconds: b-roll, product motion, character scenes. Wan 3.0 Video Prime is the same model family tuned for speed at a higher rate; this row is the standard one. To animate a local or private image, upload it first (POST /v1/uploads, or create\_upload over MCP, or goro run --image) and pass the handle as image\_handle. Billed per second of video you ask for. ## Price **\$0.16 per second of video.** An exact rate, not an estimate. The total is that rate times the seconds of video in your request, so you can work out what a call costs before you make it. No call costs less than $0.0020 or more than $4.80, the price of the largest request this endpoint takes, 30 seconds of video. `discover` and `inspect` quote your exact request before it runs. ## Input | Field | Type | Required | Notes | | -------------- | ----------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `audio` | `boolean` | | Whether the output carries an audio track. Does not change the price. Defaults to `true`. | | `prompt` | `string` | yes | What to film. Describe the subject, the action, the camera move, the lighting and the mood. Chinese and English both work. | | `duration` | `integer` | | Length of the generated video in seconds. This is what you are billed on, and it is the only field that changes the price. Defaults to `5`. | | `image_urls` | `array` | | Optional. One public http or https link to the image the video should start from (JPEG, PNG or WEBP, up to 10 MB). Use this when the image is ALREADY published somewhere; the output URL of an image endpoint works directly. Nothing is copied or re-hosted, we pass the link on. For a file that is not already public, use image\_handle instead and it never has to be published anywhere. Send one of the three, never two. | | `resolution` | `720P` | | Fixed at 720P (the model spells its resolutions with a capital P). Resolution changes the price per second, and this endpoint quotes one exact rate, so 480P and 1080P would get their own endpoints. Defaults to `"720P"`. | | `aspect_ratio` | `adaptive`, `16:9`, `4:3`, `1:1`, `3:4`, `9:16` | | Shape of the output video. adaptive follows the starting image when there is one. Defaults to `"16:9"`. | | `image_base64` | `string` | | Optional, and only for a SMALL image (under 3 MB) or a caller that cannot run a shell command. The image inline, as base64 of the file's own bytes; a data:image/png;base64, prefix is accepted and its declared type ignored. Understand the cost before choosing it: this string is part of the request, and on an MCP call the request IS the tool arguments, which pass through the model's context window. A 3 MB photograph is 4 MB of base64, on the order of a million tokens, and in practice it is truncated and the run fails looking like file corruption. Do not downscale an image to fit here: use image\_handle, which takes up to 10 MB and keeps the file out of the conversation entirely. The 3 MB ceiling is 4,194,304 base64 characters and is a property of the request body, not of the model. JPEG, PNG or WEBP, checked against the file's own leading bytes rather than against any type you declare. Send one of the three, never two. | | `image_handle` | `string` | | Optional, and the RIGHT WAY to send a local or private image. Upload the file first with POST /v1/uploads (or the create\_upload MCP tool, or the CLI's --image flag, which does the whole thing for you) and put the short handle it returns here. The file goes straight from you to a private bucket: it never passes through a tool call, so it never passes through a model's context window, and up to 10 MB is fine. Nothing is published. The handle is usable for 10 minutes after the upload lands and the file is deleted 30 minutes after that. Supplying it makes this an image-to-video call, at exactly the same price. Send one of image\_handle, image\_urls and image\_base64, never two. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"video.wan","input":{"prompt":"A sailing boat crossing a bay at golden hour, drone orbit, gentle waves."}}' ``` ## Response The full response. Values are illustrative. ```json theme={null} [ "https://example.com/goro/generated-video-1.mp4" ] ``` # Wan 3.0 Video Prime Source: https://docs.usegoro.ai/tools/video/video-wan_prime Generate a video from a text prompt using Alibaba Wan 3.0 Video Prime, the fast variant, at 720P with audio, or animate a still image you supply as the first fr Generate a video from a text prompt using Alibaba Wan 3.0 Video Prime, the fast variant, at 720P with audio, or animate a still image you supply as the first frame. Clips run 2 to 30 seconds and render in a fraction of the standard model's time. Returns the URL of the generated MP4. That URL is temporary: Goro holds a run's results for 24 hours and nothing guarantees the file outlives that, so collect what you need within 24 hours. Use when turnaround matters more than the last cent: iterating on a shot, generating several candidates in one session, or feeding a pipeline that waits on the result. Same capabilities as Wan 3.0 video, faster end to end, at a higher rate per second. To animate a local or private image, upload it first (POST /v1/uploads, or create\_upload over MCP, or goro run --image) and pass the handle as image\_handle. Billed per second of video you ask for. ## Price **\$0.2520 per second of video.** An exact rate, not an estimate. The total is that rate times the seconds of video in your request, so you can work out what a call costs before you make it. No call costs less than $0.0020 or more than $7.56, the price of the largest request this endpoint takes, 30 seconds of video. `discover` and `inspect` quote your exact request before it runs. ## Input | Field | Type | Required | Notes | | -------------- | ----------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `audio` | `boolean` | | Whether the output carries an audio track. Does not change the price. Defaults to `true`. | | `prompt` | `string` | yes | What to film. Describe the subject, the action, the camera move, the lighting and the mood. Chinese and English both work. | | `duration` | `integer` | | Length of the generated video in seconds. This is what you are billed on, and it is the only field that changes the price. Defaults to `5`. | | `image_urls` | `array` | | Optional. One public http or https link to the image the video should start from (JPEG, PNG or WEBP, up to 10 MB). Use this when the image is ALREADY published somewhere; the output URL of an image endpoint works directly. Nothing is copied or re-hosted, we pass the link on. For a file that is not already public, use image\_handle instead and it never has to be published anywhere. Send one of the three, never two. | | `resolution` | `720P` | | Fixed at 720P (the model spells its resolutions with a capital P). Resolution changes the price per second, and this endpoint quotes one exact rate, so 480P and 1080P would get their own endpoints. Defaults to `"720P"`. | | `aspect_ratio` | `adaptive`, `16:9`, `4:3`, `1:1`, `3:4`, `9:16` | | Shape of the output video. adaptive follows the starting image when there is one. Defaults to `"16:9"`. | | `image_base64` | `string` | | Optional, and only for a SMALL image (under 3 MB) or a caller that cannot run a shell command. The image inline, as base64 of the file's own bytes; a data:image/png;base64, prefix is accepted and its declared type ignored. Understand the cost before choosing it: this string is part of the request, and on an MCP call the request IS the tool arguments, which pass through the model's context window. A 3 MB photograph is 4 MB of base64, on the order of a million tokens, and in practice it is truncated and the run fails looking like file corruption. Do not downscale an image to fit here: use image\_handle, which takes up to 10 MB and keeps the file out of the conversation entirely. The 3 MB ceiling is 4,194,304 base64 characters and is a property of the request body, not of the model. JPEG, PNG or WEBP, checked against the file's own leading bytes rather than against any type you declare. Send one of the three, never two. | | `image_handle` | `string` | | Optional, and the RIGHT WAY to send a local or private image. Upload the file first with POST /v1/uploads (or the create\_upload MCP tool, or the CLI's --image flag, which does the whole thing for you) and put the short handle it returns here. The file goes straight from you to a private bucket: it never passes through a tool call, so it never passes through a model's context window, and up to 10 MB is fine. Nothing is published. The handle is usable for 10 minutes after the upload lands and the file is deleted 30 minutes after that. Supplying it makes this an image-to-video call, at exactly the same price. Send one of image\_handle, image\_urls and image\_base64, never two. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"video.wan_prime","input":{"prompt":"A cyclist crossing a rain-slick city street at night, neon reflections, tracking shot."}}' ``` ## Response The full response. Values are illustrative. ```json theme={null} [ "https://example.com/goro/generated-video-1.mp4" ] ``` # Clone a voice Source: https://docs.usegoro.ai/tools/voice/voice-clone Create a reusable voice model from one or more recordings of a person speaking, and get back a voice_id you can pass to voice.speak. Requires an explicit consen Create a reusable voice model from one or more recordings of a person speaking, and get back a voice\_id you can pass to voice.speak. Requires an explicit consent attestation on every call. Use when you need the SAME custom voice repeatedly: a narrator for a series, a brand voice, a character. Reference recordings arrive two ways, mixable in one call, 1 to 20 in total: sample\_urls, public http or https links; or sample\_handles, for recordings the user has on their device or can make on the spot. For handles, call create\_upload with content\_type "audio/\*" and give the user upload\_page\_url: that page lets them RECORD with their microphone right there, or pick an audio file, and the exact format is read from the file itself. At least ten seconds of clear speech per clip; 30 to 60 seconds of natural speech in a quiet room gives the closest match, and several varied clips beat one. The voice belongs to your workspace and no other workspace can use it. It stays available until you delete it with voice.delete. You must set consent\_attested to true, which is a statement that the person whose voice this is has agreed to it being cloned: read the field description before you send it, and do not send it on a voice you have no consent for. ## Price **Free.** This call is not charged. It places no hold, it draws nothing from your balance, and it works on an empty wallet. Nothing to reconcile afterwards and no ceiling to read. ## Input | Field | Type | Required | Notes | | ------------------ | --------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `label` | `string` | yes | A name for this voice so you can recognise it later in voice.list. Not used by the model, only for your own reference. | | `sample_urls` | `array` | | Public http or https links to recordings of the SAME speaker. At least ten seconds of clear speech per clip; several clips capture more range than one. Links to private, internal or loopback addresses are refused. Up to 25 MB per file and 100 MB in total. May be combined with sample\_handles; together they must name 1 to 20 recordings. | | `sample_handles` | `array` | | Upload handles for recordings of the SAME speaker, from create\_upload with content\_type "audio/\*". The upload page lets the user record with their microphone on the spot or pick an audio file (MP3, WAV, M4A, OGG, WEBM or FLAC, up to 10 MB each); redeem each handle within 10 minutes of its upload finishing. May be combined with sample\_urls; together they must name 1 to 20 recordings. | | `consent_attested` | `boolean` | yes | Must be set to true. By setting it you attest that the person whose voice these recordings capture has given documented consent to have their voice cloned and synthesised for this use, and that you can produce that consent on request. A recording being public is not consent, and neither is owning the rights to the recording: those are different permissions and you need the one from the person. What you attest and when is recorded against the voice. See the voice clause of the Acceptable Use Policy. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"voice.clone","input":{"label":"Narrator, warm male","consent_attested":true}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "label": "Narrator, warm male", "status": "ready", "voice_id": "a1b2c3d4e5f60718293a4b5c", "created_at": "2026-01-02T09:30:00.000Z", "sample_count": 3, "consent_statement": "The person whose voice these recordings capture has given documented consent to have their voice cloned and synthesised for this use, and I can produce that consent on request. A recording being public is not consent, and neither is owning the rights to the recording.", "consent_attested_at": "2026-01-02T09:30:00.000Z" } ``` # Delete a voice Source: https://docs.usegoro.ai/tools/voice/voice-delete Permanently delete one of your cloned voices, at the provider as well as here. After this the voice_id stops working and nothing further is charged for keeping Permanently delete one of your cloned voices, at the provider as well as here. After this the voice\_id stops working and nothing further is charged for keeping it. Use when a cloned voice is no longer needed, or when the person it belongs to withdraws consent. This is not reversible and there is no way to recover the model afterwards: cloning again means supplying the reference recordings again. A voice\_id belonging to another workspace is answered exactly like one that does not exist. ## Price **Free.** This call is not charged. It places no hold, it draws nothing from your balance, and it works on an empty wallet. Nothing to reconcile afterwards and no ceiling to read. ## Input | Field | Type | Required | Notes | | ---------- | -------- | -------- | -------------------------------------------------------------------------------- | | `voice_id` | `string` | yes | The id of a voice this workspace owns, as returned by voice.clone or voice.list. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"voice.delete","input":{"voice_id":"a1b2c3d4e5f60718293a4b5c"}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "label": "Narrator, warm male", "status": "deleted", "voice_id": "a1b2c3d4e5f60718293a4b5c", "deleted_at_provider": true } ``` # List your voices Source: https://docs.usegoro.ai/tools/voice/voice-list List the voice models this workspace owns, with the id to pass to voice.speak, when each was created and what consent was attested for it. List the voice models this workspace owns, with the id to pass to voice.speak, when each was created and what consent was attested for it. Use when you need the voice\_id of a voice you cloned earlier, or to check what this workspace is still paying to keep. Takes no input. Only ever returns voices belonging to the caller's own workspace. ## Price **Free.** This call is not charged. It places no hold, it draws nothing from your balance, and it works on an empty wallet. Nothing to reconcile afterwards and no ceiling to read. ## Input | Field | Type | Required | Notes | | ----- | ---- | -------- | ----- | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"voice.list","input":{}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "label": "Narrator, warm male", "status": "ready", "voice_id": "a1b2c3d4e5f60718293a4b5c", "created_at": "2026-01-02T09:30:00.000Z", "sample_count": 3, "consent_statement": "The person whose voice these recordings capture has given documented consent to have their voice cloned and synthesised for this use, and I can produce that consent on request. A recording being public is not consent, and neither is owning the rights to the recording.", "consent_attested_at": "2026-01-02T09:30:00.000Z" } ``` # Text to speech Source: https://docs.usegoro.ai/tools/voice/voice-speak Turn text into natural speech and get back a link to the audio file. Choose the model, the output format and the speaking rate. Turn text into natural speech and get back a link to the audio file. Choose the model, the output format and the speaking rate. Use when you need spoken audio from text: a voiceover line, a narration clip, an announcement, or an audio version of something you wrote. Up to 2,000 characters per call, about two minutes of speech. The audio is returned as a link that works for 24 hours, not as inline data, so download it if you need to keep it. Billed on the UTF-8 byte length of your text, so the exact price is known before the call. To speak in a voice you cloned with voice.clone, pass its voice\_id; you can only use voices your own workspace owns. As a rule of thumb, a minute of spoken audio comes to roughly \$0.045. ## Price **\$0.0450 per 1,000 UTF-8 bytes of text (roughly one minute of speech).** An exact rate, not an estimate. The total is that rate times the UTF-8 bytes of text in your request, so you can work out what a call costs before you make it. No call costs less than $0.0020 or more than $0.36, the price of the largest request this endpoint takes, 8,000 UTF-8 bytes of text. `discover` and `inspect` quote your exact request before it runs. ## Input | Field | Type | Required | Notes | | ---------------- | -------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `text` | `string` | yes | The text to speak. Billed on its UTF-8 byte length, so non-Latin scripts cost more per character than English does. | | `model` | `s2.1-pro`, `s2-pro`, `s1` | | Which voice model to use. All three cost the same. Defaults to `"s2.1-pro"`. | | `format` | `mp3`, `opus` | | Audio format of the file you get back. Uncompressed formats are not offered. Defaults to `"mp3"`. | | `prosody` | `object` | | Delivery adjustments. | | `prosody.speed` | `number` | | Speaking rate. 1 is normal, 0.5 is half speed, 2 is double. Defaults to `1`. | | `prosody.volume` | `number` | | Loudness adjustment in dB. Defaults to `0`. | | `voice_id` | `string` | | Speak in one of your own cloned voices, as returned by voice.clone or voice.list. Leave it out to use the stock voice. A voice\_id belonging to another workspace is refused exactly as an unknown one is. | | `normalize` | `boolean` | | Expand numbers, dates and abbreviations before speaking them. Leave on unless you have already written the text exactly as it should sound. Defaults to `true`. | | `mp3_bitrate` | `64`, `128`, `192` | | MP3 quality in kbps. Ignored for opus. Defaults to `128`. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"voice.speak","input":{"text":"Your order has shipped and should arrive on Thursday."}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "bytes": 73977, "model": "s2.1-pro", "format": "mp3", "audio_url": "https://storage.example.com/voice-output/workspace/run.mp3?token=signed-and-expiring", "expires_at": "2026-01-02T09:30:00.000Z", "utf8_bytes": 50, "content_type": "audio/mpeg" } ``` # Speech to text Source: https://docs.usegoro.ai/tools/voice/voice-transcribe Transcribe an audio file into text, with the detected language, the total duration and per-segment timings when you ask for them. Transcribe an audio file into text, with the detected language, the total duration and per-segment timings when you ask for them. Use when you have a recording and need the words: a voice note, an interview, a podcast, a meeting, a video's audio track. Give a public https link to the file. Billed per second of audio, rounded up, so set max\_duration\_seconds to roughly how long the recording is and the hold on your wallet matches. ## Price **\$0.0003 per second of audio.** An exact rate, not an estimate. The total is that rate times the seconds of audio in your request, so you can work out what a call costs before you make it. No call costs less than $0.0020 or more than $1.08, the price of the largest request this endpoint takes, 3,600 seconds of audio. `discover` and `inspect` quote your exact request before it runs. ## Input | Field | Type | Required | Notes | | ---------------------- | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `language` | `string` | | Language hint as an ISO code, e.g. en or fr. Leave it out to let the model detect the language. | | `audio_url` | `string` | yes | Public http or https link to the audio file. Links to private, internal or loopback addresses are refused. Up to 50 MB. | | `include_timestamps` | `boolean` | | Return per-segment start and end times. Adds latency on clips under 30 seconds. Defaults to `false`. | | `max_duration_seconds` | `integer` | | Roughly how long the recording is. This sizes the hold on your wallet; you are billed for the real duration, which is usually less. Audio longer than this is still transcribed but is billed at this ceiling. Defaults to `600`. | These are all the fields this endpoint accepts. Anything else is rejected as a `validation_error` rather than forwarded to the provider. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"voice.transcribe","input":{"audio_url":"https://example.com/media/team-standup.mp3"}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "text": "Right, that is everything from me. I will send the notes over this afternoon.", "language": "english", "segments": [ { "end": 2.31, "text": "Right, that is everything from me.", "start": 0 } ], "language_code": "en", "billed_seconds": 7, "duration_seconds": 6.42 } ``` # Web page scrape Source: https://docs.usegoro.ai/tools/web/web-scrape Fetches one or more web pages and returns each page title plus its cleaned text content, with navigation, headers, footers and cookie banners stripped out. Work Fetches one or more web pages and returns each page title plus its cleaned text content, with navigation, headers, footers and cookie banners stripped out. Works on any public URL and is tied to no particular site. Use when you have URLs and need what is actually on those pages, and no site-specific tool fits: an article, a docs page, a pricing or about page, or a link another tool handed you. Not a search engine, so give it URLs rather than a query, and reach for web.search or news.search when you need to find the pages first. One result, and one billable item, per URL, including URLs that fail to load. ## Price **\$0.005970 per result.** That is the rate you pay for each result the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer results costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ------ | ------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `urls` | `array` | yes | Pages to fetch. Full URLs ([https://example.com/pricing](https://example.com/pricing)) or bare hostnames (example.com), which are resolved to https for you. Each URL is fetched exactly as given and no links are followed, so this reads pages rather than crawling sites. One result, and one billable item, per URL, INCLUDING a URL that cannot be loaded: that one comes back with success false, pageTitle null and text "Scrape Failed", and is charged the same as a successful one. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"web.scrape","input":{"urls":["example"]}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "text": "How we cut p99 latency by 60 percent Published 12 March 2026 Our checkout API used to spend most of its time waiting on a single lookup. This post walks through how we found it, what we changed, and the numbers before and after. Finding it We started with traces rather than dashboards, because the average looked fine and only the tail hurt...", "success": true, "pageTitle": "How we cut p99 latency by 60 percent", "originalUrl": "https://example.com/blog/how-we-cut-latency" } ``` # Website audit Source: https://docs.usegoro.ai/tools/web/website-audit Technical and on-page SEO audit of one website, page by page, in a real browser. Each page comes back with a 0 to 100 score and nine category scores, plus meta Technical and on-page SEO audit of one website, page by page, in a real browser. Each page comes back with a 0 to 100 score and nine category scores, plus meta tags (title and description with lengths, canonical, robots, Open Graph, Twitter card, viewport, language), heading structure, content signals (word count, readability, keyword density, thin content, search intent), technical checks (HTTPS and certificate, mobile friendliness, sitemap, robots.txt, JSON-LD, microdata and RDFa types, AMP, indexability, whether the page needs JavaScript to render its content, pagination), page timing measured in the browser (time to first byte, first contentful paint, total blocking time, cumulative layout shift, speed index), links (internal and external counts, anchor text quality, broken links and redirect targets with the status code each returned), image alt and size coverage, and basic accessibility (ARIA labels, lang attribute, skip links, contrast). Every issue is classified critical, warning or info and carries an id, a category, a priority, an estimated impact and a fix hint. A final item of type site-summary averages the pages, ranks the issues that repeat across them, and reports crawl coverage. Use when you have a website and need to know what is actually wrong with it: auditing a prospect before pitching, checking a site after a launch or migration, or turning a domain into a concrete fix list. One site per call, so pass a single URL and call again for the next one. Billed per PAGE audited, not per site: leaving maxPages out audits up to 5 pages, and whatever you set is what you pay for, up to 25. It does NOT tell you what a site is built on (no CMS, framework, analytics or hosting detection) and returns nothing about backlinks, keyword rankings or traffic. ## Price **\$0.12 per page.** That is the rate you pay for each page the call returns, so the total depends on how many it produces. Billing follows actual usage, so a call that returns fewer pages costs less, and a call that costs nothing to serve is free. `discover` and `inspect` also return a ceiling for your specific request, which is a maximum you will never be charged above. ## Input | Field | Type | Required | Notes | | ------------------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `maxPages` | `integer` | | How many pages to crawl and audit, and therefore how many pages you are billed for. The actor audits 5 when you leave it out. A site with fewer pages than this bills only the pages it found. 25 is Goro's ceiling, not the actor's: past it a crawl can outlive this endpoint's 20 minute run limit. | | `startUrls` | `array` | yes | The one site to audit, as a URL string or an object with a url. One site per call: the page cap below is applied per start URL, so a second URL would double what you are billed while the quote still sized itself on one. Call again for the next site. | | `auditLinks` | `boolean` | | Internal and external link counts, anchor text quality, broken links and redirect targets with status codes. On unless you set it false. | | `crawlPages` | `boolean` | | Follow internal links from the start URL and audit more pages of the same site. The actor does this when you leave it out. Set it false to audit only the exact URL you passed, which bills one page. | | `auditImages` | `boolean` | | Alt attributes, dimensions, file size, format, lazy loading. On unless you set it false. | | `auditSchema` | `boolean` | | JSON-LD, microdata and RDFa detection with the schema.org types found. On unless you set it false. | | `auditContent` | `boolean` | | Word count, readability, keyword density, thin content, search intent. On unless you set it false. | | `auditHeadings` | `boolean` | | Heading hierarchy H1 to H6, multiple H1s, skipped levels. On unless you set it false. | | `auditMetaTags` | `boolean` | | Title, description, Open Graph, Twitter card, canonical. On unless you set it false. Turning a section off makes the output smaller, never cheaper: the price is per page audited whatever it contains. | | `auditTechnical` | `boolean` | | HTTPS and certificate, canonical, robots meta, sitemap, robots.txt, mobile friendliness, indexability, JavaScript rendering, AMP, pagination. On unless you set it false. | | `maxConcurrency` | `integer` | | Pages audited in parallel. The actor uses 5. Lower it on a site that rate-limits you. | | `auditPerformance` | `boolean` | | Page timing measured in the browser: time to first byte, first contentful paint, total blocking time, cumulative layout shift, speed index. On unless you set it false. Note it does not measure LCP or INP. | | `respectRobotsTxt` | `boolean` | | Skip URLs the site's robots.txt disallows. The actor does this when you leave it out. | | `includeSubdomains` | `boolean` | | Also crawl subdomains of the start URL, e.g. blog.site.com when starting from site.com. Off unless you set it. | Goro forwards your input to the underlying tool unchanged, so any field the tool accepts works here even if it is not listed above. ## Example ```bash theme={null} curl -X POST https://api.usegoro.ai/v1/run \ -H "Authorization: Bearer $GORO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"slug":"website.audit","input":{"startUrls":["example"],"maxPages":3}}' ``` ## Response One row of the response. Values are illustrative. ```json theme={null} { "type": "page", "audit": { "url": "https://example-store.test/collections/lamps", "links": { "broken": { "urls": [ "https://example-store.test/collections/discontinued" ], "count": 1, "verified": [ { "url": "https://example-store.test/collections/discontinued", "error": "Not Found", "statusCode": 404 } ] }, "external": { "count": 4, "sample": [ "https://instagram.com/example-store" ], "dofollow": 1, "nofollow": 3 }, "internal": { "count": 84, "sample": [ "https://example-store.test/products/brass-floor-lamp" ], "unique": 71, "inFooter": 18, "inContent": 44, "inNavigation": 22 }, "redirects": { "urls": [ { "url": "https://example-store.test/lamps", "status": 301, "redirectUrl": "https://example-store.test/collections/lamps" } ], "count": 1 }, "anchorTexts": { "empty": 2, "generic": 5, "descriptive": 77 } }, "title": "Lamps and lighting", "images": { "total": 31, "images": [ { "alt": "Brass floor lamp with linen shade", "url": "https://example-store.test/media/brass-floor-lamp.jpg", "title": "", "width": 1200, "format": "jpg", "hasAlt": true, "height": 1600, "sizeKB": 412, "isLarge": true, "fileName": "brass-floor-lamp.jpg", "hasTitle": false } ], "withAlt": 19, "optimized": 24, "withTitle": 0, "withoutAlt": 12, "largeImages": 3 }, "issues": { "info": [ "No Twitter card tags" ], "critical": [ "12 images are missing alt text" ], "warnings": [ "Meta description is 187 characters (optimal: 120-160)" ], "structured": { "info": [], "critical": [ { "id": "IMAGES_MISSING_ALT", "fixHint": "Add descriptive alt text to every content image.", "message": "12 images are missing alt text", "category": "images", "priority": 8, "pagesAffected": [ "https://example-store.test/collections/lamps" ], "estimatedImpact": "high" } ], "warnings": [ { "id": "DESCRIPTION_TOO_LONG", "fixHint": "Trim the meta description to 120-160 characters so it is not cut off in results.", "message": "Meta description is 187 characters (optimal: 120-160)", "category": "meta", "priority": 3, "pagesAffected": [ "https://example-store.test/collections/lamps" ], "estimatedImpact": "medium" } ] } }, "content": { "intent": "transactional", "length": 3184, "wordCount": 512, "readability": { "level": "Fairly easy", "score": 58, "recommendation": "Readability is acceptable for a product listing page." }, "thinContent": false, "sentenceCount": 31, "characterCount": 3184, "keywordDensity": { "topKeywords": [ { "count": 16, "density": 3.12, "keyword": "lamps" }, { "count": 10, "density": 1.95, "keyword": "lighting" } ], "keywordRelevanceScore": 82 }, "paragraphCount": 9, "recommendation": "Content length is fine. Add a short intro paragraph above the grid.", "h1TitleConsistency": { "h1Text": "Lamps and lighting", "titleText": "Lamps and lighting", "consistent": true }, "averageCharsPerWord": 5.21, "averageWordsPerSentence": 16.52 }, "headings": { "h1": { "count": 1, "texts": [ "Lamps and lighting" ], "optimal": true }, "h2": { "count": 3, "texts": [ "Floor lamps", "Table lamps" ] }, "h3": { "count": 6, "texts": [ "Brass finish" ] }, "h4": { "count": 0, "texts": [] }, "h5": { "count": 0, "texts": [] }, "h6": { "count": 0, "texts": [] }, "structure": { "valid": true, "issues": [], "hierarchy": [ { "tag": "h1", "text": "Lamps and lighting", "level": 1 }, { "tag": "h2", "text": "Floor lamps", "level": 2 } ] } }, "metaTags": { "title": { "value": "Lamps and lighting", "length": 18, "unique": true, "optimal": false, "present": true, "lengthPixels": 118, "recommendation": "Title too short (18 characters, ~118px). Recommended: 30-60 characters." }, "robots": { "value": "index, follow", "noindex": false, "present": true, "nofollow": false, "directives": [ "index", "follow" ] }, "charset": { "value": "utf-8", "present": true }, "language": { "value": "en", "present": true }, "viewport": { "valid": true, "value": "width=device-width, initial-scale=1", "present": true }, "canonical": { "url": "https://example-store.test/collections/lamps", "present": true, "selfReferencing": true }, "openGraph": { "url": "https://example-store.test/collections/lamps", "type": "website", "image": "", "title": "Lamps and lighting", "present": true, "complete": false, "description": "Floor, table and pendant lighting." }, "description": { "value": "Browse our full range of floor lamps, table lamps and pendant lighting, with next day delivery on everything in stock and a two year guarantee on every fitting we sell.", "length": 187, "unique": true, "optimal": false, "present": true, "lengthPixels": 1204, "recommendation": "Description too long. Recommended: 120-160 characters." }, "twitterCard": { "present": false, "complete": false } }, "technical": { "amp": { "present": false }, "https": { "valid": true, "present": true, "protocol": "https:", "certificateValid": true }, "sitemap": { "url": "https://example-store.test/sitemap.xml", "valid": true, "present": true, "sources": [ { "url": "https://example-store.test/sitemap.xml", "foundIn": "robots.txt", "validXml": true, "statusCode": 200 } ], "accessible": true }, "rendering": { "contentMissingWithoutJS": false, "usesJavaScriptRendering": true, "internalLinksMissingWithoutJS": true }, "robotsTxt": { "url": "https://example-store.test/robots.txt", "valid": true, "content": "User-agent: *\nDisallow: /cart\nSitemap: https://example-store.test/sitemap.xml", "present": true, "accessible": true }, "pagination": { "hasNext": true, "hasPrev": false, "pageNumber": 1, "isPaginated": true }, "indexability": { "reason": null, "signals": { "metaRobots": "index, follow", "xRobotsTag": null, "canonicalUrl": "https://example-store.test/collections/lamps", "canonicalStatusCode": 200 }, "indexable": true }, "mobileFriendly": { "responsive": true, "viewportValid": true, "viewportPresent": true, "isMobileFriendly": true }, "structuredData": { "rdfa": false, "types": [ "BreadcrumbList", "ItemList" ], "jsonLd": true, "schemas": [ { "type": "BreadcrumbList", "format": "json-ld" } ], "microdata": false, "recommendation": "Add Product markup to the items in this listing." } }, "crawlDepth": 1, "httpStatus": 200, "performance": { "speedIndex": 2410.5, "pageLoadTime": 0, "coreWebVitals": { "cls": 0.07 }, "timeToFirstByte": 214, "totalBlockingTime": 240, "firstContentfulPaint": 1180, "cumulativeLayoutShift": 0.07 }, "overallScore": 71, "accessibility": { "skipLinks": false, "ariaLabels": 6, "colorContrast": { "issues": 2, "passed": false }, "langAttribute": true }, "categoryScores": { "links": 90, "images": 40, "schema": 50, "content": 60, "headings": 100, "metaTags": 75, "technical": 80, "performance": 65, "accessibility": 70 } }, "title": "Lamps and lighting", "pageUrl": "https://example-store.test/collections/lamps", "httpStatus": 200 } ```