GraphQL
The same graph, queried as a graph. Available to first-party surfaces today.
REST serves the questions integrations ask most: profile this domain, list the vocabulary, tell me what changed. GraphQL serves the cases where the shape of the answer is the point, when one round trip should return a store, its technologies, and their dates together.
Both read the same corpus. Neither is a cache of the other.
Access
| Endpoint | POST https://api.ongoing.ai/graphql |
| Auth | A service token issued directly by OngoingAI |
| Keys | A workspace API key does not authenticate GraphQL. oai_live_… keys are REST credentials |
| Browser IDE | Not served. The endpoint answers POST with a query, nothing else |
This is the honest state of it: GraphQL is what our own surfaces use, and access is arranged rather than self-served. If your integration needs it, write to api@ongoing.ai and say what you are building. Everything published in the REST reference is available to any workspace key today, without asking anyone.
A query
query Store($domain: String!) {
store(domain: $domain) {
domain
rank
firstSeenAt
lastScannedAt
technologies {
active
confidence
firstSeenAt
lastSeenAt
technology { slug name vendorName }
}
}
}curl https://api.ongoing.ai/graphql \
-H "Authorization: Bearer YOUR_SERVICE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"query":"{ store(domain:\"gymshark.com\"){ domain technologies { active technology { slug } } } }"}'Domains are normalized for you, so GYMSHARK.COM, www.gymshark.com, and a full product URL all resolve to the same store.
What the schema covers
store(domain)— corpus identity, rank, first and last observation, catalog summary, and technology edges with their install dates and confidence.technology(slug)— one entry from the canonical vocabulary. The same slugs REST uses.candidates(...)— the evidence queue: relationships claimed by a source and awaiting verification, cursor paginated.storeAnalysis(id)— the status of an analysis started byanalyzeStore.- Mutations — ingest relationship evidence from a capture, resolve a candidate to a domain, and queue scans. Evidence ingestion is idempotent on the key you supply, so a retry replays the original receipt rather than writing the capture twice.
Reading the answers
Two behaviours are worth knowing before you branch on a response.
An unknown domain is null, not an error. A null store means we have never observed that domain. It is not a statement that the domain is not a store, and treating it as one is the most common way to misread this API.
Absence of a technology is not removal. Edges carry active along with the dates that support it. A technology that stopped appearing is marked inactive once completed observations of comparable scope confirm it, not the first time a scan misses it. The same rule governs the REST profile, and Observations explains it in full.
Which one to use
| Use | Interface |
|---|---|
| Enrich a list of domains, in a workflow tool or a script | REST |
| One domain, one profile, in a product surface | REST |
| A shape REST does not return in one call | GraphQL |
| Writing evidence back into the graph | GraphQL |
Operations served at v1 GET
The operations this build serves, as operation id to URL. Unauthenticated and unmetered. An operation defined in the contract but not yet implemented is omitted here.
Observations and evidence
How to read freshness, scope, install states, and absence without drawing conclusions the data does not support.