Getting started
Quickstart
Submit a job, wait for completion, and download its collected results.
Request API access. Once approved, use the API token and base URL supplied by the team. Run these commands in Bash with curl and jq installed. Keep tokens in a secure local environment or server secret store, never frontend code.
1. Prepare your environment
Set NOSTIMATES_BASE_URL to the supplied origin without a trailing slash and NOSTIMATES_API_KEY to your nst_ token. For a new job, generate IDEMPOTENCY_KEY once. Keep the same key and payload when retrying an ambiguous submission; a fresh key creates new work.
export IDEMPOTENCY_KEY="$(uuidgen)"2. Submit the job
curl --fail-with-body --silent --show-error --max-time 30 \
-X POST "$NOSTIMATES_BASE_URL/v1/crawl-jobs" \
-H "Authorization: Bearer $NOSTIMATES_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-d '{
"prompt": "What are the best CRM tools for a small team?",
"targets": [
"google",
"chatgpt",
"perplexity"
],
"runCount": 1,
"options": {
"country": "US"
}
}' \
--output accepted.json{
"accepted": true,
"crawlJobId": "8a1d1ac4-1bf4-4f2b-9c9c-bda9d2356e72",
"replayed": false,
"status": "queued"
}The request writes accepted.json. It acknowledges a persisted asynchronous job, not a collected answer. Repeating the same validated request with the same account and key returns the original job with replayed: true. A changed request under that key returns 409; do not assume reordered target arrays are equivalent.
3. Poll until terminal
The following loop uses a five-second interval and a five-minute client deadline. These are example client choices, not contractual latency or request-rate limits. Each HTTP request is bounded by the remaining deadline. Keep the job ID if the deadline expires; the server job continues and can be checked later.
# Requires Bash, curl and jq. These are client choices, not API limits.
set -euo pipefail
JOB_ID=$(jq -er '.crawlJobId' accepted.json)
POLL_INTERVAL=5
POLL_TIMEOUT=300
deadline=$((SECONDS + POLL_TIMEOUT))
status=queued
while (( SECONDS < deadline )); do
remaining=$((deadline - SECONDS))
curl --fail-with-body --silent --show-error --max-time "$remaining" \
"$NOSTIMATES_BASE_URL/v1/crawl-jobs/$JOB_ID" \
-H "Authorization: Bearer $NOSTIMATES_API_KEY" --output status.json
status=$(jq -er '.status' status.json)
case "$status" in
completed) break ;;
failed) echo "Job failed. Inspect status.json and contact the team." >&2; exit 1 ;;
queued|running|finalizing) ;;
*) echo "Unexpected status: $status" >&2; exit 1 ;;
esac
remaining=$((deadline - SECONDS))
if (( remaining <= 0 )); then break; fi
sleep_for=$POLL_INTERVAL
if (( sleep_for > remaining )); then sleep_for=$remaining; fi
sleep "$sleep_for"
done
if [[ "$status" != completed ]]; then
echo "Polling deadline reached. Keep JOB_ID; resume polling later." >&2
exit 1
fi{
"id": "8a1d1ac4-1bf4-4f2b-9c9c-bda9d2356e72",
"prompt": "What are the best CRM tools for a small team?",
"targets": [
"google",
"chatgpt",
"perplexity"
],
"requestedAt": "2026-10-09T10:00:00.000Z",
"status": "running",
"result": null,
"progress": {
"completedRuns": 1,
"failedRuns": 0,
"runningRuns": 2,
"totalRuns": 3
}
}{
"id": "8a1d1ac4-1bf4-4f2b-9c9c-bda9d2356e72",
"prompt": "What are the best CRM tools for a small team?",
"targets": [
"google",
"chatgpt",
"perplexity"
],
"requestedAt": "2026-10-09T10:00:00.000Z",
"status": "completed",
"result": {
"url": "/v1/crawl-jobs/8a1d1ac4-1bf4-4f2b-9c9c-bda9d2356e72/result"
},
"progress": {
"completedRuns": 2,
"failedRuns": 1,
"runningRuns": 0,
"totalRuns": 3
}
}completed means the final artifact is available, including when some or all collections failed. failed is a parent-level terminal failure. The status response contains progress and a result URL, not the full result.
4. Download and inspect
curl --fail-with-body --silent --show-error --max-time 60 \
--location --max-redirs 3 \
"$NOSTIMATES_BASE_URL/v1/crawl-jobs/$JOB_ID/result" \
-H "Authorization: Bearer $NOSTIMATES_API_KEY" \
--output result.json &&
jq '{failedRuns, responses}' result.jsonUse the owning account token. Storage may issue a short-lived redirect; curl --location follows it without forwarding the Authorization header to a different host. Never use --location-trusted or manually send the account token to a storage host. Use the HTTPS base URL supplied during onboarding.
{
"requestId": "8a1d1ac4-1bf4-4f2b-9c9c-bda9d2356e72",
"prompt": "What are the best CRM tools for a small team?",
"targets": [
"google",
"chatgpt",
"perplexity"
],
"runCount": 1,
"requestedAt": "2026-10-09T10:00:00.000Z",
"updatedAt": "2026-10-09T10:02:00.000Z",
"completedAt": "2026-10-09T10:02:00.000Z",
"failedRuns": {
"google": 0,
"chatgpt": 0,
"perplexity": 1
},
"responses": {
"google": [
{
"runNumber": 1,
"runTaskId": "7f5c0888-f5ad-4661-b07b-79fbbe1c7b31",
"status": "completed",
"capturedAt": "2026-10-09T10:01:00.000Z",
"pageUrl": "https://www.google.com/search?q=example",
"aioverview": null,
"serp": {
"organic": [
{
"position": 1,
"snippet": "Illustrative search result summary.",
"title": "Illustrative CRM comparison",
"url": "https://example.com/crm-comparison"
}
],
"peopleAlsoAsk": [],
"shopping": [],
"sponsored": []
}
}
],
"chatgpt": [
{
"runNumber": 1,
"runTaskId": "9a7536e7-b610-420a-a34c-5f829956f4d3",
"status": "completed",
"capturedAt": "2026-10-09T10:01:00.000Z",
"text": "Illustrative captured answer text.",
"markdown": null,
"pageUrl": "https://chatgpt.com/",
"sources": []
}
],
"perplexity": []
}
}This example has two collected responses and one failed Perplexity run. An empty response array caused by a failed collection is not evidence that a brand was absent. Preserve failedRuns alongside the data.