PRūFPRūF

PRūF Data API

Your first API request.

Connect ingredient lists, allergen disclosures and nutrition to restaurant menus. Explore one restaurant or download a complete data release.

1. Create an API key

Sign in to your account with an active API subscription. Give your key a name and save it when it appears. The full key is shown only once.

Keep it on your server, in an environment variable or secret manager. Send it in the Authorization header. Never place it in a browser app, a public repository or a URL. You can revoke keys from your account.

2. Find your latest release

A snapshot is a fixed version of the data. This request returns its ID, source dates and restaurant coverage for your plan.

curl --fail-with-body \
  -H "Authorization: Bearer $PRUF_API_KEY" \
  "https://prufapp.com/api/data/v1/snapshots/latest"

Keep the returned snapshot_id for subsequent requests. Source dates describe when each record was collected; a release date does not mean every restaurant was scraped that day.

3. Read menu items

Save this example as pruf-example.mjs and run it with Node.js. It reads every page for the first restaurant in your release. Choose another restaurant using its ID from coverage.

// Node.js 18+. Set PRUF_API_KEY in your server environment.
const base = "https://prufapp.com/api/data/v1";
const key = process.env.PRUF_API_KEY;
if (!key) throw new Error("Set PRUF_API_KEY first");

async function get(path) {
  const response = await fetch(base + path, {
    headers: { Authorization: "Bearer " + key }
  });
  if (!response.ok) {
    throw new Error(response.status + ": " + await response.text());
  }
  return response.json();
}

const release = await get("/snapshots/latest");
const restaurant = release.coverage[0];
if (!restaurant) throw new Error("No restaurants in this release");

let cursor;
do {
  const query = new URLSearchParams({
    snapshot_id: release.snapshot_id,
    restaurant_id: restaurant.restaurant_id,
    limit: "100"
  });
  if (cursor) query.set("cursor", cursor);
  const page = await get("/items?" + query);
  console.log(JSON.stringify(page.data, null, 2));
  cursor = page.next_cursor;
} while (cursor);

Pages contain up to 100 items. Pass next_cursor unchanged to fetch the next page; a null cursor means you have reached the end. Cursors belong to one restaurant and snapshot.

4. Download JSON or CSV

Set SNAPSHOT_ID to the ID from step 2. Downloads include all restaurants in that release. Change format=csv to format=json for JSON.

curl --fail-with-body \
  -H "Authorization: Bearer $PRUF_API_KEY" \
  "https://prufapp.com/api/data/v1/snapshots/$SNAPSHOT_ID/download?format=csv" \
  -o pruf-data.csv

CSV cells containing lists or objects use JSON text. The release metadata describes the CSV encoding, including spreadsheet-safe escaping.

Read the data as disclosed

  • A null nutrition value means it was not disclosed, not that it is zero.
  • none_declared and source_not_disclosed are different allergen states. Neither is a guarantee against cross-contact.
  • Ingredient text preserves alternatives such as “canola and/or soybean oil.” A text match does not prove every alternative is present.

Limits and errors

Each account has 30 requests per minute, 10,000 requests per calendar month and 30 downloads per calendar month. A download also counts as one request. Monthly limits reset at midnight UTC on the first day of the month. Keys on the same account share these limits.

ResponseWhat to do
400Check the query fields, IDs, page size and cursor.
401Check your API key. Missing, invalid or revoked keys cannot access data.
403Check your subscription and account access.
404Check the endpoint and coverage. For snapshot_not_found, fetch the latest release and restart pagination.
429Respect Retry-After for the minute limit, or reset_at for the monthly limit.
503The service is temporarily unavailable. Try again later; contact support if it continues.

Need help? Contact support with the endpoint and error code. Never include your API key.