Docs
Quickstart
Go from signup to a Decide verdict on a restaurant you already know.
Vegan dishes and supporting evidence for US restaurants, for travel planners and AI assistants.
1. Get your key — it's on the next screen
Sign up and your working API key appears immediately, with copy-paste examples — no email round-trip. It starts with a provisional allowance. Confirming your email later upgrades the same key to the confirmed free allowance, with provisional usage counted toward that allowance. The confirmation link is valid for 24 hours and works on any device; the key keeps working in the browser where you signed up, and the dashboard there needs a normal sign-in. Sandbox keys start with cmp_test_. Paid tiers use cmp_live_. Send the key in X-Compass-API-Key.
Your key opens all supported data endpoints. Search, Enrich, and Decide are the core workflow; Changes supports synchronization. Allowance and rate limits apply on every tier without reducing response fidelity.
Cost per call: Search: 1 credit (fast) or 2 (rich, default) · Decide: 2 (fast) or 5 (rich, default) · Enrich: 1. Only successful calls use credits. Each response reports its cost in the X-Compass-Credits-Charged header.
2. Call Search
Set COMPASS_API_KEY in your shell or runtime environment, or paste your key below to fill in the examples on this page. Use X-Compass-Mode: fast (1 credit) unless you need evidence fields (rich = 2 credits).
Try it with curl, an HTTP client like Postman or Insomnia, or your AI assistant through MCP.
Stays in this page only. Without it, the examples use $COMPASS_API_KEY.
Start with a natural-language restaurant search.
$env:COMPASS_API_KEY = "<YOUR_COMPASS_API_KEY>"
$headers = @{
"X-Compass-API-Key" = $env:COMPASS_API_KEY
"X-Compass-Mode" = "fast"
}
$body = @'
{
"query": "vegan ramen in New York",
"limit": 5
}
'@
Invoke-RestMethod `
-Method Post `
-Uri "https://api.compassfoodtechnologies.com/v1/search" `
-Headers $headers `
-ContentType "application/json" `
-Body $bodyThen Decide, an HTTP client, or MCP
Paste your key in place of $COMPASS_API_KEY (or set it as an environment variable).
Search first: "vegan ramen in New York"
Fast mode: 1 credit per call.
curl -s -X POST "https://api.compassfoodtechnologies.com/v1/search" \
-H "Content-Type: application/json" \
-H "X-Compass-API-Key: $COMPASS_API_KEY" \
-H "X-Compass-Mode: fast" \
-d '{"query":"vegan ramen in New York","limit":5}'Then Decide on one restaurant
Fast mode: 2 credits per call.
curl -s -X POST "https://api.compassfoodtechnologies.com/v1/decision/restaurant-fit" \
-H "Content-Type: application/json" \
-H "X-Compass-API-Key: $COMPASS_API_KEY" \
-H "X-Compass-Mode: fast" \
-d '{"compass_id":"42911d00f964a520f2231fe3","user_profile":{"diet":"strict_vegan"}}'HTTP client (Postman, Insomnia)
Fast mode: 1 credit per call.
POST https://api.compassfoodtechnologies.com/v1/search
Content-Type: application/json
X-Compass-API-Key: paste-your-key-here
X-Compass-Mode: fast
{
"query": "vegan ramen in New York",
"limit": 5
}AI assistant through MCP
{
"mcpServers": {
"compass": {
"command": "npx",
"args": [
"-y",
"@compassfoodtech/mcp"
],
"env": {
"COMPASS_API_KEY": "paste-your-key-here"
}
}
}
}3. Confirm a successful response
A successful Search call returns HTTP 200. This abridged preview answers the With location request: one restaurant with its matching vegan ramen dishes and the date the menu was collected.
Abridged SearchResponse example — 31 of 204 contract leaves. Illustrative values, not a live payload.
{
"compass_request_id": "req_example_quickstart",
"coverage": {
"status": "in_coverage",
"covered_geography": "US"
},
"results": [
{
"compass_id": "cmp_example_ramen_house_001",
"name": "Example Ramen House",
"dish_match": {
"requested_dish": "ramen",
"match_type": "dish",
"dishes": [
{
"name": "Shoyu Vegan Ramen",
"category": "Pasta & Noodles",
"match_type": "variant",
"is_vegan": true
},
{
"name": "Spicy Miso Vegan Ramen",
"category": "Pasta & Noodles",
"match_type": "variant",
"is_vegan": true
}
]
},
"vegan_dining": {
"dishes_availability": "yes",
"vegan_dish_count": 6,
"matching_dish_count": 2,
"matched_dishes": [
{
"name": "Shoyu Vegan Ramen",
"category": "Pasta & Noodles",
"match_type": "variant",
"evidence_id": "ev_dish_example_shoyu_vegan_ramen",
"vegan_basis": "validated_menu_classification"
},
{
"name": "Spicy Miso Vegan Ramen",
"category": "Pasta & Noodles",
"match_type": "variant",
"evidence_id": "ev_dish_example_spicy_miso_vegan_ramen",
"vegan_basis": "validated_menu_classification"
}
],
"truncated": false,
"menu_collected_at": "2026-10-07T16:00:00Z",
"menu_collected_at_status": "known",
"match_status": "matched"
},
"match_for_profile": {
"decision": "fit",
"confidence": "medium",
"reason_codes": [
"REQUESTED_DISH_AVAILABLE"
],
"risk_flags": [],
"verification_required": true,
"recommended_user_text": "Compass found a vegan version of the requested dish on the collected menu (menu collected 2026-10-07)."
}
}
],
"result_status": "matched",
"total_results": 1,
"ai_generated": true
}If you see 401, check the API key header. A 403 provisional_key_expired means the grace period ended — confirm your email from the recovery page to reactivate the same key. Revoked or suspended keys cannot be restored by confirmation. A temporary 429 rate_limit_exceeded includes Retry-After; an exhausted allowance is monthly_limit_exceeded and will not succeed if you retry immediately.
4. Enrich a restaurant you already know
Match one of your own records. Read compass_id from the response. If matched is false or the ID is null, stop before Decide.
curl -sS -X POST "https://api.compassfoodtechnologies.com/v1/enrich/restaurant" \
-H "Content-Type: application/json" \
-H "X-Compass-API-Key: $COMPASS_API_KEY" \
-d '{
"name": "Example Ramen House",
"address": "123 Example Street, Brooklyn, NY"
}'Abridged EnrichResponseMatched example — 31 of 76 contract leaves. Illustrative values, not a live payload.
{
"compass_request_id": "req_example_ramen_house_enrich",
"matched": true,
"identity_status": "resolved",
"match_confidence": 0.97,
"compass_id": "cmp_example_ramen_house_001",
"restaurant": {
"compass_id": "cmp_example_ramen_house_001",
"name": "Example Ramen House",
"vegan_score": {
"overall": 70,
"scoring_version": "v5.0-menu",
"base": 70,
"vegan_main_count": 4,
"vegan_dish_count": 6,
"is_fully_vegan": false,
"vegan_adj": 0,
"vegan_adj_reason": "not_enough_reviews",
"quality_adj": 0,
"quality_adj_reason": "no_standout",
"scored_at": "2026-10-23T12:00:00.000Z"
},
"vegan_dining": {
"dishes_availability": "yes",
"vegan_dish_count": 6,
"matching_dish_count": 2,
"matched_dishes": [
{
"name": "Shoyu Vegan Ramen",
"category": "Pasta & Noodles",
"match_type": "variant",
"evidence_id": "ev_dish_example_shoyu_vegan_ramen",
"vegan_basis": "validated_menu_classification"
},
{
"name": "Spicy Miso Vegan Ramen",
"category": "Pasta & Noodles",
"match_type": "variant",
"evidence_id": "ev_dish_example_spicy_miso_vegan_ramen",
"vegan_basis": "validated_menu_classification"
}
],
"truncated": false,
"menu_collected_at": "2026-10-07T16:00:00Z",
"menu_collected_at_status": "known",
"match_status": "matched"
}
},
"ai_generated": true
}After a confirmed match, store the compass_id with your record — enrich once, decide per profile within your allowance.
5. Decide: get the verdict
Call Decide with the ID Enrich returned, not a placeholder. The script below does that, and it refuses to continue when the match fails. It calls Decide in rich mode (the default), which costs 5 credits; fast mode costs 2.
const MAX_ATTEMPTS = 4;
const MAX_ELAPSED_MS = 30000;
const JITTER_MS = 250;
const baseUrl = "https://api.compassfoodtechnologies.com";
const apiKey = process.env.COMPASS_API_KEY;
if (!apiKey) throw new Error('Set COMPASS_API_KEY');
const started = Date.now();
async function compassCall(path, body, extraHeaders = {}) {
let attempts = 0;
while (attempts < MAX_ATTEMPTS) {
if (Date.now() - started > MAX_ELAPSED_MS) throw new Error('stop: elapsed bound');
attempts += 1;
let response;
try {
response = await fetch(baseUrl + path, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Compass-API-Key': apiKey,
...extraHeaders,
},
body: JSON.stringify(body),
signal: AbortSignal.timeout(10000),
});
} catch (error) {
if (error?.name === 'TimeoutError' || error?.name === 'AbortError') {
throw new Error('stop: ambiguous timeout after dispatch');
}
continue;
}
if (response.status === 429) {
const problem = await response.json();
if (problem.error === 'monthly_limit_exceeded') throw new Error('stop: allowance exhausted');
if (problem.error !== 'rate_limit_exceeded' && problem.error !== 'ip_rate_limit_exceeded') {
throw new Error('stop: unexpected 429');
}
if (attempts >= MAX_ATTEMPTS) throw new Error('stop: rate limit attempts exhausted');
const retryAfter = Number(response.headers.get('Retry-After') ?? '1');
const waitMs = Math.max(0, retryAfter) * 1000 + Math.floor(Math.random() * JITTER_MS);
if (Date.now() + waitMs - started > MAX_ELAPSED_MS) throw new Error('stop: elapsed bound');
await new Promise((resolve) => setTimeout(resolve, waitMs));
continue;
}
if (response.status === 401 || response.status === 403) throw new Error('stop: auth');
if (response.status === 400 || response.status === 422) throw new Error('stop: validation');
if (response.status === 404) {
const problem = await response.json();
if (problem.error === 'restaurant-not-found') {
throw new Error('stop: unresolved id; keep your record and re-resolve');
}
throw new Error('stop: not found');
}
if (!response.ok) throw new Error('stop: unexpected status ' + response.status);
return response.json();
}
throw new Error('stop: attempt bound');
}
const enrich = await compassCall('/v1/enrich/restaurant', {
name: 'Example Ramen House',
address: '123 Example Street, Brooklyn, NY',
});
if (enrich.matched !== true || typeof enrich.compass_id !== 'string' || !enrich.compass_id) {
console.log('No confirmed match. Do not call Decide. Review candidates instead of taking the first one.');
console.log(enrich.match_candidates ?? []);
process.exit(2);
}
// Persist only the returned compass_id, next to your own restaurant id.
const persistedId = enrich.compass_id;
const verdict = await compassCall('/v1/decision/restaurant-fit', {
compass_id: persistedId,
user_profile: { diet: 'strict_vegan' },
}, { 'X-Compass-Mode': 'rich' });
console.log(verdict.decision);
A fit for strict_vegan means the collected menu has a vegan meal:
Abridged DecisionResponse example — 8 of 26 contract leaves. Illustrative values, not a live payload.
{
"compass_request_id": "req_example_ramen_house_decide_fit",
"compass_id": "cmp_example_ramen_house_001",
"decision": "fit",
"confidence": "medium",
"reason_codes": [
"VEGAN_MEAL_AVAILABLE"
],
"risk_flags": [],
"recommended_user_text": "Compass found a vegan meal on the collected menu (menu collected 2026-10-07).",
"verification_required": true,
"ai_generated": true
}A restaurant with a few vegan dishes, but not enough for a meal, returns unknown:
Abridged DecisionResponse example — 9 of 26 contract leaves. Illustrative values, not a live payload.
{
"compass_request_id": "req_example_noodle_bar_decide_unknown",
"compass_id": "cmp_example_noodle_bar_002",
"decision": "unknown",
"confidence": "low",
"reason_codes": [
"VEGAN_OPTIONS_LIMITED"
],
"risk_flags": [
"limited_evidence"
],
"recommended_user_text": "Compass found vegan dishes on the collected menu, but not enough for a meal (menu collected 2026-10-07).",
"verification_required": true,
"ai_generated": true
}unknown is a valid answer when evidence is insufficient, conflicting, or stale. It is not the same as a restaurant ID that no longer resolves. The full place-matching and discovery chains are on Integration patterns.
6. Run the example locally
If you are using the JavaScript tab, create a local Node file named first-call.mjs with that example, then run it with npm:
npm init -y npm pkg set type=module node first-call.mjs
Response fields to inspect first
| Field | Type | Notes |
|---|---|---|
| results[] | array | Ranked restaurants for the submitted query. |
| results[].dish_match | object | With a dish request: the matching dish names, up to five. Compass never substitutes another dish. |
| results[].vegan_dining | object | Vegan dishes found on the collected menu, up to five, and menu_collected_at, the date that menu was collected. |
| results[].match_for_profile | object | Decision, confidence, reason codes, and risk flags when a profile is sent. |
| evidence[] | array | Source pointers used by rich responses. |
| source_freshness | object | Age of the oldest and newest supporting signal. |
unknown is a valid conservative decision when evidence is insufficient, conflicting, or stale. Treat it as guidance to verify details with the restaurant.
Terminology
| Field | Type | Notes |
|---|---|---|
| Account plan | quota and features | Controls monthly quota, endpoint access, support level, and billing. |
| Evidence tier | source strength | Describes the source quality and freshness behind an evidence item. |