REST API Reference
The Fond REST API lets you retrieve analytics data and trigger runs programmatically. All endpoints live under /api/v1/.
Authentication
Every request must include an API key. You can create keys from the dashboard at Settings > API Keys (admin/owner role required).
Pass the key in either header:
Authorization: Bearer sk-your-key-here
X-API-Key: sk-your-key-here
Keys are shown once at creation. Store them securely — they cannot be retrieved later.
Error responses
All auth failures return 401:
{ "error": "Invalid API key" }
This applies to missing, invalid, revoked, and expired keys.
Endpoints
List websites
GET /api/v1/websites
Returns all websites with setup status COMPLETE in the organization.
Response
{
"websites": [
{
"id": "clx...",
"url": "https://example.com",
"name": "Example Store",
"createdAt": "2026-01-15T10:30:00.000Z"
}
]
}
List runs
GET /api/v1/websites/:websiteId/runs
Query parameters
| Param | Type | Default | Description |
|---|---|---|---|
status | string | — | Filter by status: PENDING, RUNNING, COMPLETED, COMPLETED_WITH_ERRORS, FAILED |
limit | integer | 20 | Results per page (1-100) |
offset | integer | 0 | Number of results to skip |
Response
{
"runs": [
{
"id": "clx...",
"runDate": "2026-03-06T00:00:00.000Z",
"status": "COMPLETED",
"triggeredBy": "MANUAL",
"totalQueries": 24,
"totalResults": 72,
"failedResults": 0,
"locale": "en-US",
"startedAt": "2026-03-06T12:00:00.000Z",
"completedAt": "2026-03-06T12:05:30.000Z",
"createdAt": "2026-03-06T12:00:00.000Z"
}
],
"total": 42,
"limit": 20,
"offset": 0
}
Trigger a run
POST /api/v1/websites/:websiteId/runs
Starts a new query run for the website. Returns 409 if a run is already in progress.
Request body (optional, JSON)
| Field | Type | Description |
|---|---|---|
name | string | Optional label for the run (1-100 chars) |
queryIds | string[] | Run only specific queries. Omit to run all. |
Example
curl -X POST https://your-domain.com/api/v1/websites/clx.../runs \
-H "Authorization: Bearer sk-your-key" \
-H "Content-Type: application/json" \
-d '{"name": "Weekly check"}'
Response 201 Created
{
"id": "clx...",
"status": "RUNNING",
"triggeredBy": "MANUAL",
"totalQueries": 24,
"totalResults": 0,
"failedResults": 0,
"startedAt": "2026-03-06T14:00:00.000Z",
"completedAt": null
}
Error responses
| Status | Reason |
|---|---|
400 | Invalid body or no valid queries to run |
404 | Website not found or not in your organization |
409 | A run is already in progress |
Get run details
GET /api/v1/websites/:websiteId/runs/:runId
Returns a single run with summary statistics.
Response
{
"id": "clx...",
"runDate": "2026-03-06T00:00:00.000Z",
"status": "COMPLETED",
"triggeredBy": "MANUAL",
"totalQueries": 24,
"totalResults": 72,
"failedResults": 0,
"locale": "en-US",
"modelVersions": { "gemini": "gemini-2.0-flash", "openai": "gpt-5-mini" },
"repetitions": 3,
"startedAt": "2026-03-06T12:00:00.000Z",
"completedAt": "2026-03-06T12:05:30.000Z",
"errorMessage": null,
"createdAt": "2026-03-06T12:00:00.000Z",
"summary": {
"totalResults": 72,
"avgVisibilityScore": 34.5,
"mentionCount": 45,
"citationCount": 12
}
}
List results
GET /api/v1/websites/:websiteId/runs/:runId/results
Returns paginated query results for a run.
Query parameters
| Param | Type | Default | Description |
|---|---|---|---|
provider | string | — | Filter by provider (e.g. gemini, openai) |
queryId | string | — | Filter by specific query |
limit | integer | 50 | Results per page (1-100) |
offset | integer | 0 | Number of results to skip |
Response
{
"results": [
{
"id": "clx...",
"queryId": "clx...",
"provider": "gemini",
"model": "gemini-2.0-flash",
"isMentioned": true,
"isCited": false,
"isLinked": false,
"mentionPosition": 2,
"mentionContext": "...recommended by Example Store...",
"sentiment": "POSITIVE",
"extractedUrls": [],
"citations": null,
"visibilityScore": 45,
"followUpQuestions": ["What products does Example Store offer?"],
"brandInFollowUp": true,
"confidence": 0.85,
"groundingUrls": ["https://example.com/products"],
"errorMessage": null,
"createdAt": "2026-03-06T12:01:00.000Z",
"query": {
"text": "best online stores for electronics",
"queryType": "CATEGORY",
"categoryTier": "SAFETY",
"queryFocus": "PRODUCT"
}
}
],
"total": 72,
"limit": 50,
"offset": 0
}
Response fields
Each object in results[]:
| Field | Type | Description |
|---|---|---|
id | string | Result ID. |
queryId | string | ID of the query this result answers. |
provider | string | LLM provider (gemini, openai, claude, perplexity). |
model | string | Specific model used (e.g. gemini-2.0-flash). |
isMentioned | boolean | Whether the brand was mentioned in the response. |
isCited | boolean | Whether the response cited the brand as a source. |
isLinked | boolean | Whether the response linked to the brand. |
mentionPosition | integer | null | Which third of the response the brand first appears in: 0 = top third, 1 = middle, 2 = lower. null when not mentioned. Not a paragraph index, despite the name. (Results from before 2026-06-13 may carry a raw paragraph index instead.) |
mentionContext | string | null | Verbatim excerpt of the sentence where the brand first appears. |
sentiment | string | null | Sentiment toward the brand: POSITIVE, NEUTRAL, or NEGATIVE. null when not mentioned. |
extractedUrls | string[] | URLs found in the response text. |
citations | object | null | Provider-supplied citations (shape varies by provider); null if none. |
visibilityScore | integer | Per-result visibility score (0–100), combining mention, position, citation, link, and sentiment. 0 when not mentioned. |
followUpQuestions | string[] | Follow-up / related questions the response suggests. |
brandInFollowUp | boolean | Whether the brand appears in any follow-up question. |
confidence | number | null | Confidence in the brand-mention classification (0–1), when available. |
groundingUrls | string[] | URLs the provider used to ground its answer (web-search results). |
errorMessage | string | null | Populated if the provider call failed for this result; null otherwise. |
createdAt | string | ISO 8601 timestamp. |
query | object | The query this result answers (fields below). |
query object:
| Field | Type | Description |
|---|---|---|
text | string | The query text sent to the provider. |
queryType | string | One of BRAND_REPUTATION, CATEGORY, COMPARISON, PROBLEM_SOLVING, LOCAL, BRAND_IDENTITY. |
categoryTier | string | Intent tier: SAFETY, STRONG_REACH, REACH, or FAR_REACH. |
queryFocus | string | PRODUCT or BRAND. |
Top-level, total is the count of results matching the filter (for pagination); limit and offset echo the request.
Pagination
All list endpoints support limit and offset parameters and return a total count. To iterate through all results:
# Page 1
GET /api/v1/websites/:id/runs?limit=20&offset=0
# Page 2
GET /api/v1/websites/:id/runs?limit=20&offset=20
Rate limits
There are currently no enforced rate limits on the API. Be considerate with request frequency — avoid tight polling loops when checking run status.
Common workflows
Poll for run completion
# 1. Trigger a run
RUN_ID=$(curl -s -X POST .../runs -H "Authorization: Bearer $KEY" | jq -r .id)
# 2. Poll status every 30 seconds
while true; do
STATUS=$(curl -s .../runs/$RUN_ID -H "Authorization: Bearer $KEY" | jq -r .status)
echo "Status: $STATUS"
[ "$STATUS" = "COMPLETED" ] || [ "$STATUS" = "FAILED" ] && break
sleep 30
done
# 3. Fetch results
curl -s .../runs/$RUN_ID/results -H "Authorization: Bearer $KEY" | jq .
Export all results for a website
OFFSET=0
while true; do
RESPONSE=$(curl -s ".../runs/$RUN_ID/results?limit=100&offset=$OFFSET" \
-H "Authorization: Bearer $KEY")
echo "$RESPONSE" >> results.json
TOTAL=$(echo "$RESPONSE" | jq .total)
OFFSET=$((OFFSET + 100))
[ $OFFSET -ge $TOTAL ] && break
done