API Feed
The API feed is the do-it-yourself feed: give us one HTTPS URL that returns your records as JSON, and we keep a searchable list in sync with it — no connector to wait for, no export, no upload. If your system of record has a read API (or you can expose one), this makes it searchable today.
It is deliberately the simplest feed there is. We fetch your endpoint on a schedule and stage the response body exactly as it comes back; the same importer that reads a file you upload yourself reads it. We never re-shape or re-map your data — so what you get out is precisely what your endpoint returns. This page covers what’s specific to the API feed: the shape your endpoint must return, optional authentication, and how deletions work. For how feeds work in general — syncing, binding a list, deleting — see the Feeds reference.
Connect an endpoint
- Go to Feeds (under Sources in the sidebar), click Connect feed and choose API endpoint.
- Give the feed a name and paste your endpoint URL. If it needs authentication, fill in the optional auth header (name and value); leave both blank for a public endpoint. Choose which media store the synced file should live in (your built-in store by default).
- Click Connect. We call your endpoint there and then, so a wrong URL, a rejected credential, or a response that isn’t JSON is caught immediately rather than failing quietly later — and the first sync starts right away.
From here the feed keeps feeds/<name>.ndjson up to date in your media store; point a list at that file with keep-in-sync switched on and your records are searchable — the Feeds page walks through that step.
What your endpoint must return
A single GET of your URL must return your records as one of:
- a JSON array of record objects —
[{ … }, { … }]; - a single JSON object;
- NDJSON — one JSON object per line.
That’s the only requirement. Each object’s properties become fields on a record, and their roles are inferred just as they are for a JSON or NDJSON file you upload yourself — there is nothing to map. If your endpoint returns HTML, plain text, or an error page, the connect step tells you so rather than importing rubbish.
Full fidelity: the common format
For exact control — your own record IDs, declared field roles, and deletion handling — have your endpoint return the common format: NDJSON whose first line is a manifest. It’s the same format the other feeds produce and the same one you can upload by hand, so nothing here is API-feed-specific:
{"searchstack_format":1,"source":"crm","snapshot":true,"fields":{"summary":"vectorised","status":"facet"}}
{"id":"cust-1","name":"Ada Lovelace","summary":"Long-standing account","status":"active"}
{"id":"cust-2","name":"Grace Hopper","summary":"New this quarter","status":"active"}
idgives each record a stable identity, so renaming a record updates it in place instead of creating a duplicate.fieldsdeclares each field’s role (searchable, facet, resource, or vectorised) so provisioning is exact rather than guessed.snapshot: trueturns on deletion reconciliation — see below.
If you need a shape we don’t produce for you — joining two systems, renaming fields, filtering rows — that shaping is your endpoint’s job, not a mapping screen in Search Stack. Return the common format and you get full fidelity; return a plain array and it still works, just with guessed field roles and no deletions.
Keeping search in step with deletions
By default an API feed is additive: each sync adds new records and updates changed ones, but never removes anything (exactly like re-uploading a file). If your endpoint returns the complete set of records every time, add "snapshot": true to its manifest and the feed will also deactivate records that are no longer in the response — so a record you delete at the source drops out of search on the next sync. Only use snapshot when the response really is the whole set; a partial or paged response under snapshot would deactivate everything it left out.
Authentication
Authentication is optional and deliberately minimal: a single header sent on every request. Fill in the header name (for example Authorization or X-API-Key) and its value (for example Bearer … or your API key). The value is stored encrypted and never shown again — to change it, remove the feed and reconnect. The endpoint must be https so the header is never sent in the clear.
For security, the URL must be a public, named host on the standard https port — IP addresses, localhost, intranet names and custom ports are refused. If your data lives somewhere private, put a small public endpoint in front of it that returns the records.
How change is detected
On each poll we fetch the endpoint and compare the response to the last one; a byte-for-byte identical response stages nothing, so an unchanged source costs a fetch and nothing more. Any edit or deletion changes the response and triggers a fresh sync. Syncing is periodic (as often as every 5 minutes, 30 by default), so an edit becomes searchable within minutes rather than instantly — and large sources are best sharded across several narrower endpoints, one feed each.
A worked example
Say your CRM has a reporting API. You add a small read-only endpoint — https://api.acme.com/searchstack/customers — that returns your customers as common-format NDJSON with snapshot: true, protected by an Authorization header. You connect it as an API feed and paste the header. The feed mirrors the response into feeds/customers.ndjson in your built-in media store. You create a customers list, import from that file with keep-in-sync on, and now your CRM is searchable — add a customer and it appears, delete one and it drops out — with no export, no upload, and no connector to wait for.