Content API (Zap)

The one API meant to be called from a browser, with its own rules.

Zap is the suite's CMS, and its delivery API exists for the opposite reason to everything else: so somebody else's website can consume it.

Which is why it plays by different rules. Do not apply anything you learned in Shared rules here.

What differs

Rest of the suite Zap
Credential eel_sk_ / eel_at_ secret_… or public_…
From a browser No Yes, that is the point
Error envelope { error: { code, message } } { error: { code, message, status } }
Codes eight, lowercase UPPERCASE, its own set
Rate headers None X-RateLimit-*

The two key kinds

  • secret_ is for your server. It is not bound to any origin, and it is the only kind you can allow to read draft content. It allows 100 requests a minute.
  • public_ is for the browser. It is origin-restricted and allows 60 a minute.

A public key in your site's JavaScript is acceptable precisely because that is what it is for, and because the origin bounds it. A key from the rest of the suite in that same place would be a leak.

Rate headers

It is the only API in the suite that tells you how many requests you have left:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1780000000

Reset is computed from the oldest request still counting, not from now, so the window slides rather than resetting all at once.

One locale per request

Content is multilingual, but you ask for one language at a time: ?locale=en. There is no "give me every translation" mode, so a response does not grow with each language you add.

SDK and MCP server

Two npm packages, if you would rather not speak HTTP by hand:

  • @8ux-co/eelzap-api-sdk-ts, the TypeScript SDK.
  • @8ux-co/eelzap-mcp-server, an MCP server for Zap that runs over stdio.

That MCP server is not the suite one: it is a binary running on your machine, it uses Zap's keys, and it does no scope filtering, because Zap's keys have no scopes. It has no idempotency either. Do not carry one's rules over to the other.