AACSearch

Quickstart

Create an index, mint a key, upload documents and run your first search — end to end with curl.

Start in the console

After confirming your email, choose Search on a website or Search through the API. Name your search; your first workspace is created with it. The wizard has three steps: Data → Check → Connect. Website users can preview and map file fields or connect a CMS/feed. API users get an index-scoped server key and requests filled with their index parameters. Run a successful preview before connecting. Installation code or a created key does not complete setup: send a search from your site/application, then finish. You can leave and resume; advanced schema and ranking tools remain available separately.

This walkthrough assumes a console account with an organization and access to the AACSearch API.

1. Create an index

Index creation is a console action: Search → Indexes → New index. Give it a name, an optional slug (generated from the name when omitted, and suffixed -2, -3 on collision) and, if you need more than the built-in fields, a schema.

A minimal marketing-site index needs no extra fields at all. A product index usually declares the things you filter on:

{
	"name": "Documentation",
	"slug": "documentation",
	"fields": [
		{ "name": "category", "type": "string", "facet": true },
		{ "name": "published_at", "type": "int64", "sort": true },
		{ "name": "views", "type": "int32", "optional": true }
	]
}

The console validates this schema and creates a managed index. Use its logical slug in API requests:

{
	"id": "idx_01HZX8",
	"slug": "documentation",
	"name": "Documentation"
}

Field names must match ^[a-zA-Z_][a-zA-Z0-9_]{0,63}$ and must not shadow the reserved ones (id, external_id, title, content, url, source_type, organization_id, tags, indexed_at, metadata).

2. Create an API key

Search → API keys → New key. Choose the scopes to grant. The default set — search:read, documents:read, documents:write — is what an ingest job needs. Add settings:write for reindexing and scoped keys, analytics:write for click tracking.

The response contains the key exactly once:

{
	"id": "key_01HZX9",
	"name": "Ingest",
	"prefix": "aac_live_9f3c2a1b…",
	"scopes": ["search:read", "documents:read", "documents:write"],
	"createdAt": "2026-10-23T09:12:44.000Z",
	"key": "aac_live_9f3c2a1bD4eF6gH8iJ0kL2mN4oP6qR8s"
}

Production organizations get aac_live_…; outside production the generated key is aac_test_…. Only the SHA-256 hash is stored — if you lose the key, revoke it and create another.

Export it once for the commands below:

export AAC_KEY="aac_live_9f3c2a1bD4eF6gH8iJ0kL2mN4oP6qR8s"
export AAC_API="https://search.example.com/api/v1"

3. Upload documents

external_id and content are required; everything else is optional. Your own schema fields are passed through as top-level keys.

curl -sS -X POST "$AAC_API/indexes/documentation/documents" \
  -H "Authorization: Bearer $AAC_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "documents": [
      {
        "external_id": "pricing",
        "title": "Pricing",
        "content": "Free includes 10,000 searches per month. Pro is 29 USD per month with 500,000 searches.",
        "url": "https://example.com/pricing",
        "tags": ["billing"]
      },
      {
        "external_id": "limits",
        "title": "Rate limits",
        "content": "Each API key allows 600 requests per minute in a sliding window.",
        "url": "https://example.com/limits",
        "tags": ["api"]
      }
    ],
    "action": "upsert"
  }'
{
	"index": "documentation",
	"indexed": 2,
	"failed": [],
	"usage": {
		"plan": "pro",
		"planName": "Pro",
		"period": "2026-10",
		"resetsAt": "2026-11-01T00:00:00.000Z",
		"searches": { "used": 0, "limit": 500000 },
		"documents": { "used": 2, "limit": 50000 },
		"aiTokens": { "used": 0, "limit": 100000 },
		"indexes": { "used": 1, "limit": 10 },
		"apiKeys": { "used": 1, "limit": 10 },
		"connectors": { "used": 0, "limit": 10 },
		"wallet": { "balanceCents": 0, "currency": "USD" }
	},
	"request_id": "req_5b1d9c0a7e2f4a6b8c0d1e2f"
}

A batch is not all-or-nothing: rejected documents appear in failed as { "external_id", "error" } while the rest are indexed. Up to 500 documents per request.

curl -sS -G "$AAC_API/indexes/documentation/search" \
  -H "Authorization: Bearer $AAC_KEY" \
  --data-urlencode "q=pricing" \
  --data-urlencode "query_by=title,content" \
  --data-urlencode "per_page=5"
{
	"index": "documentation",
	"query": "pricing",
	"language": "en",
	"found": 1,
	"out_of": 2,
	"page": 1,
	"per_page": 5,
	"took_ms": 12,
	"engine_ms": 3,
	"hits": [
		{
			"id": "pricing",
			"score": 1754,
			"document": {
				"id": "pricing",
				"external_id": "pricing",
				"title": "Pricing",
				"content": "Free includes 10,000 searches per month. Pro is 29 USD per month with 500,000 searches.",
				"url": "https://example.com/pricing",
				"source_type": "api",
				"organization_id": "org_01h9",
				"tags": ["billing"],
				"indexed_at": 1761214364
			},
			"highlights": [
				{ "field": "title", "snippet": "<mark>Pricing</mark>", "matchedTokens": ["pricing"] }
			]
		}
	],
	"facets": [],
	"request_id": "req_9a7c3d1e5f0b2a4c6d8e0f1a"
}

Response headers carry the numbers you need for back-off and dashboards:

x-aac-quota-limit: 500000
x-aac-quota-remaining: 499999
x-aac-index: documentation
x-aac-profile: default
x-aac-charged-cents: 0
x-ratelimit-limit: 600
x-ratelimit-remaining: 599
x-ratelimit-reset: 1761214424
x-request-id: req_9a7c3d1e5f0b2a4c6d8e0f1a

A search without matches still succeeds with found: 0; it is logged as a zero-result query and shows up in /analytics/alerts.

5. Add autocomplete

curl -sS -G "$AAC_API/indexes/documentation/suggest" \
  -H "Authorization: Bearer $AAC_KEY" \
  --data-urlencode "q=pri" \
  --data-urlencode "limit=5"
{
	"index": "documentation",
	"query": "pri",
	"suggestions": [
		{
			"text": "Pricing",
			"source": "document",
			"documentId": "pricing",
			"url": "https://example.com/pricing",
			"score": 3,
			"highlight": "<mark>Pri</mark>cing"
		}
	],
	"did_you_mean": null,
	"sources": { "documents": 1, "queries": 0 },
	"cache": "miss",
	"request_id": "req_1c2d3e4f5a6b7c8d9e0f1a2b"
}

Suggest calls are rate-limited but never metered — an autocomplete that bills per keystroke would be unusable.

Where to go next

On this page