EveryAppEveryApp

DataForSEO for the Gateway

Step-by-step guide: a DataForSEO login (DATAFORSEO_API_KEY) on the Gateway for OpenSEO and the DataForSEO MCP app: the account and its balance, the key, the check, rotation.

DataForSEO is the SEO data provider behind OpenSEO: keyword metrics, SERPs, backlinks, local business and AI answer data, and scheduled rank checks. The Gateway holds one DataForSEO credential (DATAFORSEO_API_KEY) and puts it on every call OpenSEO makes to api.dataforseo.com (or the free sandbox.dataforseo.com); the app never sees it. At the end the person has a DataForSEO account with credit on it, the Gateway holds the credential, and a keyword lookup in OpenSEO returns real numbers.

Settings

NAME required or optional what it holds
DATAFORSEO_API_KEY required by OpenSEO and by the DataForSEO MCP app login:password: the API login and API password from the dashboard's API Access page, joined by a colon. The base64 of that string works too.

The API password is generated by DataForSEO and is not the account's sign-in password. The Gateway base64-encodes a value containing a colon and sends the result as HTTP Basic auth; a value without a colon is sent as is.

The same credential serves the DataForSEO MCP app, which offers DataForSEO's tools to members' AI clients through the Gateway's /mcp with no page and nothing to deploy: pnpm everyapp mcp on dataforseo --json turns it on (before or after the key is set; mcp list shows both), and members an admin grants see dataforseo_<tool> tools in their connected client.

Costs and plans

As of October 2026: a free account gets a $1 trial credit on registration, with no time limit, and the Sandbox is free for any registered user (it answers like production with made-up data). Real data is pay as you go, no subscription; the minimum top-up is $50 and funds do not expire. Daily spending limits can be set per account or per endpoint (API Settings). The docs state 10,000 API calls per minute and 30 simultaneous requests (the older FAQ still says 2,000 per minute); the account's own limits are in the user_data response. Each keyword lookup costs a few cents: a Google Ads search-volume request is $0.09 live, a DataForSEO Labs request starts at $0.012 plus $0.00012 per result, a live SERP is $0.002. Since 2026-07-01 the Backlinks API is pay as you go like the rest (DataForSEO removed its $100 monthly commitment; prices went up 20%). No card is asked for at sign-up (dataforseo.com/apis/serp-api: "No credit card required"); a card or other payment comes in only at Add Funds.

Only a person can

  • Register the DataForSEO account and accept its terms.
  • Read the API login and API password off the API Access page (or their email) and type them at the hidden prompt.
  • Add funds, pick a daily spending limit, or clear an old IP Access list.
  • Decide to replace a credential the Gateway already holds.

Before you start

  1. pnpm everyapp secret list openseo --json, from any directory (the app id is openseo; a fork may have changed the manifest's id): the DATAFORSEO_API_KEY entry in data.app.setup.unmet (names, need.kind required, holder gateway, setup setup/dataforseo.md) means this guide applies, and OpenSEO doesn't open until it is set: the Gateway lists it as needing setup, sends a browser opening https://openseo.<app base domain> to its setup dialog and answers anything else 503 app_needs_setup. data.app.setup.command is pnpm everyapp configure openseo, which asks for every missing setting (data.next repeats it; the OpenRouter key is among them while it is missing: openrouter.md; Context is optional for OpenSEO, so CONTEXT_API_KEY sits in data.app.setup.settings with set: false and never in unmet, and configure offers it after the required ones: context.md). The same entry in data.app.setup.settings with "set": true means it is set already: ask before replacing; a value can't be read back, so "replace" means the person fetches the credential again. The older fields still say the same: data.gateway.needed has { "name": "DATAFORSEO_API_KEY", "required": true, "set": … } and data.gateway.missing names it while unset. This is how an agent lands here: status (under "openseo needs setup") and secret list openseo (under the unmet setting) print Guide: https://everyapp.com/docs/agent/setup/dataforseo.md, and secret list --json carries the path as the entry's setup (in the checkout the file is landing-page/content/agent/setup/dataforseo.md). The error APP_NOT_FOUND ("openseo isn't deployed to this Gateway yet", exit 1): deploy it first. OpenSEO lives in its own repository, which publishes a release on every push: pnpm everyapp install https://github.com/every-app/open-seo --json from this checkout installs it (any repository or tarball URL works; NO_RELEASE means that one publishes none). From a clone instead: pnpm install there, then this checkout's pnpm everyapp deploy --json from that directory (a clone's own CLI copy can't deploy; its pnpm everyapp is for dev). The key can be set before that deploy all the same (pnpm everyapp configure in OpenSEO's directory reads its everyapp.config.ts instead of the registry and asks for the Gateway's settings before the first deploy; step 5's root list is then the check, and so is pnpm everyapp secret list --json in that directory).
  2. pnpm everyapp status --json: data.installed and data.claimed are true; data.gatewayUrl is https://<gateway host>; data.appBaseDomain is <app base domain>. The entry in data.apps with "id": "openseo" has "status": "active", "bound": true and the same setup as 1; while a required setting is missing data.fixes has APP_NEEDS_SETUP for it (severity warning, headline "openseo needs setup", run the same command). Not installed, or no owner: gateway.md first.

Steps

One step per message: give the step, wait for the words it ends with ("I'm in", "I can see them", "done"), then the next. Not a checklist to send at once: steps 1 and 2 catch an unconfirmed email and an IP list before the value is set.

  1. Person: Open with the one question: do they already have a DataForSEO account, or has OpenSEO run anywhere else? No account: open https://app.dataforseo.com/register and create it (goal: a DataForSEO account; the API login is whatever the API Access page shows in step 2: DataForSEO generates it, on older accounts it is the sign-up email). Confirm the email if asked: until it is confirmed the API answers 40104. When it worked, the dashboard at app.dataforseo.com opens and shows a $1 balance. Already registered: sign in at https://app.dataforseo.com. Ran elsewhere: an OpenSEO deployed outside EveryApp held the base64 form of the same credential, which works unchanged here (step 4's note), and if that deployment whitelisted its server's IP on API Access, the list has to be emptied (step 2). Wait for "I'm in".
  2. Person: Open https://app.dataforseo.com/api-access (API Access in the left menu). It shows the API login, the API password and, for the first 24 hours after registration, a ready-made Base64 string of login:password. After 24 hours the password is hidden: click Send by e-mail (goal: get the password again) and read it from the email. Leave the IP Access list on this page empty: the Gateway calls from Cloudflare's addresses, which change, and one entry makes DataForSEO refuse every other address (40207). Keep the page or email open; nothing is typed yet. Wait for "I can see them".
  3. Person: Optional now, needed before real use: Add Funds in the Billing section or the top bar (goal: credit for live calls; minimum $50), and a daily limit under API Settings, https://app.dataforseo.com/api-settings (counted per 24 hours UTC), so a runaway job stops at a number they chose. The $1 trial credit covers the free smoke check and about ten keyword lookups (at $0.09 a Google Ads request).
  4. Person: the setup rules' first way, the person types it. In a second terminal in the every-app folder: pnpm everyapp configure openseo (from OpenSEO's directory, pnpm everyapp configure, which works before its first deploy too), which asks for this key among OpenSEO's other unset settings (the OpenRouter key while it is missing: openrouter.md says where it comes from; the optional Context key is offered after, and Enter skips it), or pnpm everyapp secret set DATAFORSEO_API_KEY for just this one. At the hidden prompt (DATAFORSEO_API_KEY, under lines saying what it is for and where it comes from; secret set asks Value for DATAFORSEO_API_KEY), paste the API login, a colon, and the API password (login:password): the form to recommend, since Verify 2's check takes it as is. Nothing is echoed. It ends with the setting live on the Gateway (Set DATAFORSEO_API_KEY on the Gateway; configure then says openseo is ready. or what it still needs). Wait for "done". (The page's Base64 string, or the value an older OpenSEO held, works too; Verify 2 then needs its header = form.) Alternative, the setup rules' third way, when they want the smoke check (Verify
    1. as well: in their terminal the hidden read (bash or zsh read -rs DATAFORSEO_API_KEY && export DATAFORSEO_API_KEY, fish read -s -x DATAFORSEO_API_KEY), paste login:password, Enter, then there pnpm everyapp configure openseo --from-env --json (every unset setting whose variable is exported, at once) or pnpm everyapp secret set DATAFORSEO_API_KEY --from-env DATAFORSEO_API_KEY --json; never a typed export …=, which the shell history keeps. No DataForSEO CLI or API returns the credential, so the second way does not apply.
  5. You: pnpm everyapp secret list --json: DATAFORSEO_API_KEY is in data.gateway.names. Then the Verify section.

Verify

  1. pnpm everyapp secret list openseo --json, from anywhere: the DATAFORSEO_API_KEY entry in data.app.setup.settings has "set": true and has left data.app.setup.unmet; data.app.setup.ready is true once nothing else of OpenSEO's is missing (its OpenRouter key counts too, Context doesn't; until then command and data.next name only what is left). In the older fields, its data.gateway.needed entry has "set": true and the name left data.gateway.missing. Then pnpm everyapp status --json: the openseo entry's setup.ready is true and data.fixes has no APP_NEEDS_SETUP for it, once everything else is set. OpenSEO not deployed yet (APP_NOT_FOUND): step 5's root list is the check until it is.
  2. Smoke check, in the person's terminal (the endpoint is account-wide, so the Gateway refuses it for apps; from the terminal it is the right check). It needs the variable in that shell, holding the same form they pasted in step 4: after the hidden prompt nothing is exported, so either have them read it in hidden again for this check (the setup rules' third way: read -rs DATAFORSEO_API_KEY && export DATAFORSEO_API_KEY, paste, Enter), or let the app be the check (3). The credential reaches curl as a config file on stdin, never on the command line (printf is a shell builtin, so no process carries it): printf 'user = "%s"\n' "$DATAFORSEO_API_KEY" | curl -s -K - https://api.dataforseo.com/v3/appendix/user_data | jq '{status: .status_code, message: .status_message, balance: .tasks[0].result[0].money.balance}' Expected: "status": 20000, "message": "Ok.", and the balance in USD (1 on a fresh account). The call is free. A wrong credential prints "status": 40100 and "balance": null; the Base64 form in user = prints the same, so when the variable holds Base64 (step 4's note), give them this line instead, before reading 40100 as a wrong login or password: printf 'header = "Authorization: Basic %s"\n' "$DATAFORSEO_API_KEY" | curl -s -K - https://api.dataforseo.com/v3/appendix/user_data | jq … Then unset DATAFORSEO_API_KEY.
  3. The app: open https://<app>.<app base domain> (the id from data.apps, usually openseo), open a project, then Keyword Research (under Research in the sidebar; the URL ends in /keywords), and search one keyword. Search volume and difficulty appear; each lookup costs a few cents. A browser that lands on the Gateway's setup dialog instead (https://<gateway host>/?setup=openseo) means a required setting is still unset: Verify 1 says which. OpenSEO shows one message for a missing key and a rejected one alike: "DataForSEO isn't set up on your EveryApp gateway yet, or rejected the key it holds. An owner or admin sets it with pnpm everyapp secret set DATAFORSEO_API_KEY." Tell them apart with pnpm everyapp secret list --json: DATAFORSEO_API_KEY absent from data.gateway.names means step 4 did not land (behind the Gateway's setup gate the app doesn't open in that state, so expect the second); present means the value is wrong (Verify 2 says which way). The Gateway's log tells them apart too (4).
  4. Logs, if that helps: cf has no Worker tail (cf workers --help lists none). In the Cloudflare dashboard, the setup rules' path: Workers & Pages → every-app-gateway → Observability (Logs on older accounts) → Live for a stream, or the stored Workers Logs on the same page. Each call that reached DataForSEO is one JSON line with evt: "integration.proxy.call", capability: "dataforseo", the path, the upstream status and DataForSEO's cost; the keys are indexed, so filter on evt. A wrong value shows status: 401 there. A key the Gateway does not hold is refused before any call, so no event appears at all; evt: "integration.proxy.upstream_failed" means DataForSEO could not be reached. Without the dashboard, cf o11y telemetry query --view events --limit 50 --timeframe-from <ms> --timeframe-to <ms> --body '<query JSON>' reads the same stored logs (read-only); the query body that filters on evt is not worked out yet (unverified).

If it fails

error or symptom meaning what to do
SECRET_VALUE_REQUIRED from secret set --json Nothing was piped and there is no terminal to prompt in One of the setup rules' three ways: the person's own terminal, or --from-env
SECRET_VALUE_REQUIRED from pnpm everyapp configure openseo --json Nothing was piped (with --from-env, stdin isn't read), no variable of a missing name was found, and there is no terminal to ask in; the message names what OpenSEO still needs The person's own terminal (step 4), NAME=value lines on stdin, or the hidden read then its data.rerun, pnpm everyapp configure openseo --from-env
SECRET_SEVERAL_NAMES --from-env or a pipe with more than one name One name per call
SECRET_EMPTY The --from-env variable is empty or not exported in that shell The hidden read (the setup rules' third way) in that same terminal first, then rerun
A browser opening OpenSEO is sent to https://<gateway host>/?setup=openseo; any other request gets 503 with the body {"error":"app_needs_setup","app":"openseo"} The Gateway's setup gate: a required setting of OpenSEO is still unset (this key or the OpenRouter one; Context is optional) data.app.setup.command, pnpm everyapp configure openseo (step 4), then Verify 1
integration_not_configured (503, header x-everyapp-error) The Gateway holds no DATAFORSEO_API_KEY; OpenSEO shows it as "DataForSEO isn't set up on your EveryApp gateway yet, or rejected the key it holds. An owner or admin sets it with pnpm everyapp secret set DATAFORSEO_API_KEY." (behind the setup gate the app doesn't open in this state) Steps 4 and 5; check the name's spelling in data.gateway.names
capability_not_requested (403) The deployed manifest does not list capabilities.dataforseo OpenSEO's does; a fork must add it to everyapp.config.ts and deploy again
integration_path_not_allowed (403) The app called an account-wide path (appendix/user_data, appendix/webhook_resend, tasks_ready, id_list, errors) By design: one key serves every app. Check balance and limits from the terminal (Verify 2)
integration_method_not_allowed (405) A method other than GET or POST DataForSEO reads with GET and POST only
integration_upstream_failed (502) The Gateway could not reach DataForSEO Retry; a persisting failure is on DataForSEO's side
status_code 40100, or HTTP 401, "you are not authorized to access this resource" Wrong login or password: the sign-in password instead of the API password, a typo, whitespace Re-read API Access (or Send by e-mail), set it again (step 4); the user form needs login:password, not base64
40104 "please verify your account before using the api" The sign-up email is not confirmed yet Open DataForSEO's confirmation email, click its link, rerun Verify 2
40200 "payment required", 40210 "insufficient funds" The balance is too low for the request Add Funds (minimum $50)
40203 "the cost limit has been exceeded" The daily spending limit in API Settings was reached (counted per 24 hours UTC) Raise it at https://app.dataforseo.com/api-settings, or wait for 00:00 UTC
40202 "rate-limit per minute has been exceeded", 40209 "too many simultaneous queries" Over the per-minute or 30-concurrent limit Retry after a pause; OpenSEO's scheduled checks spread out on their own
40204 "access denied. visit plans and subscriptions" Left over from the Backlinks commitment DataForSEO removed on 2026-07-01; a funded pay-as-you-go account should not see it Contact DataForSEO, https://dataforseo.com/contact
40207 "access denied. your ip is not whitelisted" API Access has an IP list; the Gateway's Cloudflare addresses are not on it The person removes every entry under IP Access on https://app.dataforseo.com/api-access (step 2)
40201 "unusual activity" DataForSEO paused the account The person contacts DataForSEO at https://dataforseo.com/contact (a form; the page says support is available 24/7)
40400 or 40402 "invalid path" The endpoint path is wrong An app bug, not a setup problem: compare with docs.dataforseo.com
curl prints nothing, or jq reports that the input is not JSON The variable is empty, or the response is not JSON (a redirect or block page) printf … | curl -s -K - -o /dev/null -w '%{http_code}\n' … with the same stdin config to see the HTTP status; never print the variable

Rerun, rotate, remove

  • Rerun. Every step is safe to repeat: the dashboard shows what exists, configure and secret set replace the value in one new Gateway version (once OpenSEO is ready, configure's menu changes one setting at a time), and the Verify section says where to continue after an interruption.
  • Rotate. The person changes the API login in API Access (documented: the old login:password and its base64 stop working). A new API password needs DataForSEO's support (https://dataforseo.com/contact): the help center mentions regenerating it from the dashboard but documents no control for it, and Send by e-mail resends the same one. Then step 4 again with the new value, then Verify. A value that went through the chat is compromised: rotate it, then set it one of the three ways.
  • Remove. pnpm everyapp secret unset DATAFORSEO_API_KEY --json. Without --yes it exits 1 with CONFIRMATION_REQUIRED: that is the ask; rerun with --yes only once the person has said yes. OpenSEO then needs setup again: the Gateway closes it until the key is back (its manifest marks dataforseo required; status says so, with this guide's path). Then revoke on DataForSEO's side: change the API login (or have support issue a new password), or close the account if nothing else uses it.

Sources

On this page