Authentication

Authenticate requests to the Spysay API with an API key in a request header: account-wide and domain-scoped keys, and the errors a bad key gets.

Every request is authenticated with an API key. You can only target domains connected to your spysay account.

Base URL

txt
https://spysay.com/v1

API keys

Create and manage keys in the dashboard under Channels, API and MCP, or with the API keys endpoints. A key starts with spy_live_ and is shown once, when you create it: spysay stores only a hash, so a lost key can't be recovered, only replaced.

A key is either account-wide or scoped to one domain. A scoped key can read and update its own domain under /v1/domains/{hostname} and nothing else: any other endpoint answers 403 requires_unscoped_key, and another domain 403 domain_not_owned. Use an account-wide key for businesses, questions, reports and keys.

Send the key in either header:

sh
curl https://spysay.com/v1/domains \
  -H "x-spysay-api-key: <API_KEY>"

# Or as a Bearer token
curl https://spysay.com/v1/domains \
  -H "Authorization: Bearer <API_KEY>"
ts
import { createClient } from "@spysay/sdk";

const spysay = createClient({ baseUrl: "https://spysay.com", token: process.env.SPYSAY_API_KEY! });

// The SDK sends `token` as a Bearer token.
const domains = await spysay.domains.list();

When a request carries a key, the key decides the account, even in a browser that's signed in to the dashboard. A key that's wrong or revoked is refused with 401 invalid_api_key; spysay never falls back to the signed-in session.

WARNING

Keep keys server-side. Never ship them in browser code or commit them to git. To show analytics in a browser, use a public stats token.

Credits

Your plan sets how many markets and questions you can track, and includes credits each month. Work that costs something is paid for from your credit balance, first the plan's monthly credits, then any you top up. Reading data is free.

Uses credits Free
Renders that actually render (miss, or fresh=true) Cache hits (edge_hit, hit)
Each page a cache purge re-renders Listing and retrieving domains, audits, prompts and reports
Each page an audit crawls Analytics
Each provider answer to a prompt Stats and visibility reports

When the balance can't cover a request, it fails with 402 insufficient_credits, and the error includes credits_required and credits_balance. Background work reserves its credits when it starts, so an audit or a scheduled prompt run can't overdraw your balance halfway through.

Errors

Status Type Code Meaning
401 authentication_error missing_api_key No key in the request
401 authentication_error invalid_api_key Key is invalid or revoked
403 permission_error requires_unscoped_key A domain-scoped key called an account-wide endpoint
403 permission_error domain_not_owned A domain-scoped key called another domain
402 billing_error insufficient_credits The credit balance can't cover the request

See Error Codes for the full list.