Connect agents and scripts to reviewed keyword research workflows.
Discover Keywords APIs are authenticated, quota-aware, and designed to preserve shared-cache behavior for student and operator workflows.
Base URL
Production
https://discoverkeywords.coAll research endpoints require authentication. Public pages are static and do not call research APIs.
Authentication
Supported authentication for protected requests.
Every research endpoint is authenticated. Pick the credential style that matches your integration.
Bearer token
Recommended for scripts, agents, and external skill integrations.
Authorization: Bearer gk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxBearer polling
Use the same Authorization header when polling asynchronous job status.
POST /api/research/expand/status?jobId=...
Authorization: Bearer gk_live_xxxSession cookie
Used by the authenticated dashboard after a user signs in.
Cookie: session=<managed by the web app>Quota and cache
Guardrails are part of the API contract.
Predictable limits keep research fast, fair, and cache-friendly for every account.
Admin accounts are unrestricted.
Student accounts have a daily quota for combined research calls.
Shared-cache hits do not count against quota.
Public marketing pages do not trigger paid research providers.
Endpoints
Core API surfaces.
All routes are relative to the production base URL and require authentication.
Authentication
6 routes/api/auth/sign-upCreate a student account with an invite code.
/api/auth/sign-inSign in and receive a managed session cookie.
/api/auth/accessCheck account role, trial status, quota, and block status.
/api/auth/keysGenerate an API key for integrations.
/api/auth/keysList active API keys for the current account.
/api/auth/keysRevoke an API key.
Research
6 routes/api/research/expandSubmit keyword expansion jobs.
/api/research/expand/statusAdvance an owned expansion job and return its status.
/api/research/compareCompare keyword groups and trend movement.
/api/research/compare/statusAdvance an owned compare job and return its status.
/api/research/serpRun guarded SERP analysis for validated research flows.
/api/research/trendsSubmit trend checks.
BYOK real-time pipeline
8 routes/api/research/byok/readinessCheck verified Provider connections, budget, and concurrency.
/api/research/byok/pipeline/expand/quoteQuote multi-seed expansion and semantic filtering.
/api/research/byok/pipeline/expand/executeConfirm one aggregate quote and start the private expansion job.
/api/research/byok/pipeline/compare/quoteQuote a private comparison of up to 50 keywords.
/api/research/byok/pipeline/compare/executeConfirm one aggregate quote and start the private compare job.
/api/research/byok/pipeline/jobs/{jobId}Poll an owner-scoped BYOK pipeline job and read its result.
/api/research/byok/pipeline/jobs/{jobId}/retry/quoteQuote only failed or partial stages; successful Provider work is reused.
/api/research/byok/pipeline/historyList owner-scoped BYOK pipeline history.
Game and discovery
3 routes/api/game-keywordsRead reviewed game keyword opportunities.
/api/integrations/discovery-feedRead the protected discovery feed for integrations.
/api/research/keyword-suggestionsRequest guarded keyword suggestions.
BYOK API flow
Quote, explicitly confirm, then poll.
Create a dedicated dashboard API key with byok:execute. Provider credentials are saved and verified in account settings and are never sent in research API payloads. Reuse the same Idempotency-Key when retrying a request after a network failure.
TOKEN="gk_live_..."
BASE="https://discoverkeywords.co"
curl -H "Authorization: Bearer $TOKEN" \
"$BASE/api/research/byok/readiness"
curl -X POST "$BASE/api/research/byok/pipeline/expand/quote" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: expand-example-001" \
-d '{"keywords":["ai resume builder"],"days":90,"filterTerms":["news"]}'
# Copy quoteId, requestHash, and estimatedCostUsd from the quote response.
curl -X POST "$BASE/api/research/byok/pipeline/expand/execute" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: execute-expand-example-001" \
-d '{"quoteId":"...","requestHash":"...","confirmedEstimatedCostUsd":0.016}'
curl -H "Authorization: Bearer $TOKEN" \
"$BASE/api/research/byok/pipeline/jobs/JOB_ID"
# If status is partial, request an additional quote for failed stages only.
curl -X POST "$BASE/api/research/byok/pipeline/jobs/JOB_ID/retry/quote" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: retry-expand-example-001" \
-d '{}'
# Confirm the returned retry quote through the same expand/execute endpoint.
# Successful stages are loaded from private checkpoints and are not repurchased.
curl -H "Authorization: Bearer $TOKEN" \
"$BASE/api/research/byok/pipeline/history?limit=20"const headers = {
Authorization: `Bearer ${process.env.DISCOVER_KEYWORDS_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
};
const quoted = await fetch(
"https://discoverkeywords.co/api/research/byok/pipeline/compare/quote",
{ method: "POST", headers, body: JSON.stringify({
keywords, benchmark: "gpts", days: 90,
}) },
).then((response) => response.json());
// Show quoted.quote.estimatedCostUsd to the operator before executing.
const job = await fetch(
"https://discoverkeywords.co/api/research/byok/pipeline/compare/execute",
{ method: "POST", headers: { ...headers,
"Idempotency-Key": crypto.randomUUID() },
body: JSON.stringify({
quoteId: quoted.quote.quoteId,
requestHash: quoted.quote.requestHash,
confirmedEstimatedCostUsd: quoted.quote.estimatedCostUsd,
}) },
).then((response) => response.json());Contract and retry rules
A quote includes the aggregate upper bound, expiry, batch count, and Provider/stage cost summary. Supply either days or a dateFrom/dateTo pair. Poll until the Job is complete, partial, or failed; a partial Job can still contain a standards-compatible result. For a partial Job, call its retry/quote endpoint and explicitly confirm that quote through the matching expand/execute or compare/execute endpoint.
Reuse the exact same Idempotency-Key and request body after a timeout. Reusing a key with different content returns IDEMPOTENCY_CONFLICT. Other stable rejection codes include QUOTE_EXPIRED, COST_CONFIRMATION_MISMATCH, DAILY_BUDGET_EXCEEDED, CONCURRENCY_LIMIT_REACHED, and JOB_NOT_FOUND.
gk_live_* identifies the Discover Keywords caller; Provider Connections hold the user's encrypted DataForSEO/OpenRouter credentials; byok:execute authorizes that API key to spend the owner's Provider allowance. Provider Connection lifecycle operations remain cookie-only in account settings, and Provider secrets are never accepted by these research endpoints.
Protected APIs, public documentation.
Use API keys from the authenticated dashboard. Admin, cron, D1, and shared-cache workflows remain unchanged.
Developer FAQ
Operational details for safe API use.
Answers to the most common integration questions.
Where do API keys come from?
API keys are generated from the authenticated dashboard. Public documentation pages do not create, expose, or validate keys.
Which authentication method should scripts use?
Bearer tokens are recommended for scripts, agents, and server-side integrations because they avoid leaking keys through URLs.
Do shared-cache hits count against quota?
No. Shared-cache hits are treated separately from paid research work and do not count against student daily research quota.
How should async research jobs be consumed?
Submit the job through the protected endpoint, store the returned job id, and POST the matching status endpoint until cached final results are ready. Legacy GET execution remains temporarily available during migration.
Can unauthenticated visitors call research APIs from this page?
No. The docs page is static and public, but research endpoints remain authenticated and quota-aware.
Do API docs change cron, D1, or background workflows?
No. This page documents the existing surfaces and does not modify cron jobs, D1 schema, shared cache, or provider call behavior.