Quickstart

Create a key, confirm it works, and read your first store profile.

A shield with a check, a padlock, and an id card

Create an API key

In the dashboard, open Settings → API keys and create a key. Pick a label that names the consumer ("Clay production", "Local testing") and the scopes it needs. For this walkthrough, read:stores is enough.

The full key is shown once, at creation. Store it in your secret manager: afterwards the dashboard shows only its prefix, so a key you lose has to be revoked and replaced.

export ONGOINGAI_API_KEY="oai_live_…"

Confirm the key

GET/v1/account is free and tells you what the key can do.

curl https://api.ongoing.ai/v1/account \
  -H "Authorization: Bearer $ONGOINGAI_API_KEY"

Read two things from the response: key.scopes, which decides what you may call, and plan.limits, which decides how much.

A 401 here means the key is wrong, revoked, or expired. A 200 confirms the key is valid. Later calls can still fail for their own reasons, so check the scopes and limits before a bulk run.

Example account response

The plan and limits below are one example, not what every account receives. Yours are authoritative on your own response.

{
  "workspace": { "id": "ws_01J9X4K6ZP3M8Q2R5T7V9W1Y3A", "name": "Acme Agency" },
  "key": {
    "id": "key_01J9X4K6ZP3M8Q2R5T7V9W1Y3A",
    "label": "Local testing",
    "prefix": "oai_live_a1b2",
    "environment": "live",
    "scopes": ["read:stores"],
    "expires_at": null
  },
  "plan": {
    "slug": "pro",
    "limits": {
      "rpm": 120,
      "concurrency": 5,
      "enrich_batch_max": 100,
      "freshness_days": 30,
      "history_days": 365,
      "live_refreshes_per_period": 100
    }
  },
  "period": {
    "starts_at": "2026-09-01T00:00:00Z",
    "ends_at": "2026-10-01T00:00:00Z",
    "included": {
      "lookup_cached": 10000,
      "lookup_live": 100,
      "history_query": 10000,
      "refresh_requested": 0
    },
    "used": {
      "lookup_cached": 0,
      "lookup_live": 0,
      "history_query": 0,
      "refresh_requested": 0
    }
  }
}

Read a store

GET/v1/stores/{domain} takes a hostname and canonicalizes it. A full URL works too, but must be percent-encoded, because the domain is a single path segment; the parameter reference shows that form.

curl https://api.ongoing.ai/v1/stores/gymshark.com \
  -H "Authorization: Bearer $ONGOINGAI_API_KEY"

An excerpt of a real response, trimmed to one platform, two technologies, and the observation:

{
  "match": "matched",
  "store": {
    "domain": "gymshark.com",
    "platform": { "slug": "shopify", "name": "Shopify", "confidence": "high" },
    "status": { "state": "active_storefront", "as_of": "2026-09-07T04:06:05.068Z" },
    "technologies": [
      { "slug": "attentive", "name": "Attentive", "category": "sms-marketing", "install_state": "active", "confidence": "high", "last_confirmed_at": "2026-09-01T04:39:18.315Z" },
      { "slug": "cloudflare", "name": "Cloudflare", "category": "cdn", "install_state": "active", "confidence": "high", "last_confirmed_at": "2026-09-01T04:39:18.315Z" }
    ],
    "observation": {
      "last_completed_scan_at": "2026-09-07T04:06:05.068Z",
      "scope": "homepage_only",
      "coverage_note": "Observed on the storefront homepage only. A technology that loads on product, cart, or checkout pages may not appear here."
    },
    "recent_signals": []
  }
}

That is the shape of every answer: a store, the technologies we saw, and when we saw them. Three fields deserve a look before you use the rest:

  • observation.last_completed_scan_at and observation.scope: when we last saw the site and how much of it.
  • technologies[].install_state and last_confirmed_at: whether each technology was loading, and when that was last confirmed.
  • recent_signals: the last five confirmed changes, each with effective_at and confirmed_at.

This call costs one lookup_cached unit. A 404 not_found costs nothing and means the domain is not in the corpus.

Enrich a batch

POST/v1/stores/enrich answers up to your plan's enrich_batch_max domains in one request and preserves input order. 100 is the contract maximum; the free plan allows 25.

curl https://api.ongoing.ai/v1/stores/enrich \
  -H "Authorization: Bearer $ONGOINGAI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: quickstart-batch-1" \
  -d '{"domains": ["gymshark.com", "allbirds.com", "notastore.example"]}'

Each result has a match outcome. matched and stale results include the store and are billed. no_match results carry a reason and are free, and inputs that collapse to the same domain are answered individually but billed once.

Generate a new Idempotency-Key for each new batch, then reuse that key only when retrying that same batch. A retry within 24 hours replays the original response with Idempotent-Replayed: true and bills nothing. Reusing a key with a different set of domains returns 409 conflict, which is the API catching a moved batch boundary rather than answering with the wrong rows.

Pick a client

The clients page shows the same calls through the TypeScript SDK, plain fetch, and Python, with the retry and pacing rules already applied.

Next

On this page