Membership API
The Membership API returns is-member verdicts for one identifier, across the realtime audiences in the key’s workspace. It is a serve-by-key surface: you ask about one person, by one identifier, and you get verdicts back — never attributes. It is meant to be called from the page it personalizes, so it is the one Zeotap read a browser may make directly.
A server that already reads the Profile API usually wants the other shape: ?include=audiences there returns attributes and member audiences in one response, on the serving key it already holds. Use this endpoint when membership is the whole question, when you need a definitive answer for one named audience, or when the caller is a browser.
Experimental — in active development. The endpoint and its response shape may change before general availability.
Endpoint
GET /v1/members/{index}/{value}| Parameter | Required | Description |
|---|---|---|
index | Yes | The identity field to look up, for example user_id or anonymous_id. |
value | Yes | The identifier value to check. |
?audience | No | Narrows the report to a single audience id, and makes the answer definitive — that audience’s verdict comes back whether it is true or false. Omit it to sweep the workspace, which reports positives only. |
The base URL is the Membership API host for your deployment — https://membership.zeotap.com by default. The exact URL for your workspace, with a copyable request, is shown on Personalize → Membership → Membership API.
Authentication
Pass a membership key as a Bearer token. Membership keys carry a pk_ prefix; one key answers for every realtime audience in its workspace, and the workspace is taken from the key — there is no workspace header. Create keys on Personalize → Membership → API keys.
A membership key is not a secret: it identifies the workspace and authorises is-member reads only. That is why the app shows it in full, with a Copy button, for as long as it exists rather than revealing it once — the key is meant to end up in the page source of the site that calls the endpoint, where anyone can read it anyway. Membership keys do not expire and are not rotated. Create as many as you have places to call from, and revoke one from the same tab when you want it to stop serving.
curl -H 'Authorization: Bearer <MEMBERSHIP_KEY>' \
'https://membership.zeotap.com/v1/members/user_id/u_123'Platform credentials are refused here, whatever scopes they carry: an sk_ MCP key, an rk_ REST key, and the Profile API’s svk_ serving key each answer 401. The endpoint checks the class of the credential before it checks anything else, because no grant of scope turns a secret into a public identifier.
Calling it from a browser
The endpoint serves permissive CORS: Access-Control-Allow-Origin: *, the methods GET, OPTIONS, an allowed Authorization header, and an OPTIONS preflight answered 204 and cacheable for 24 hours. There is no origin allow-list to register your domains in, and no cookies or credentials are exchanged — the key on the Authorization header is all the endpoint knows about the caller.
const res = await fetch(
"https://membership.zeotap.com/v1/members/user_id/u_123",
{ headers: { Authorization: "Bearer <MEMBERSHIP_KEY>" } },
);
// 200 → { memberships: [{ audience_id, is_member }] }
const { memberships } = await res.json();
const inCartAbandoners = memberships.some(
(m) => m.audience_id === "aud_cart_abandoners",
);Response
A 200 OK returns the audiences the person is in:
{
"memberships": [
{ "audience_id": "aud_cart_abandoners", "is_member": true },
{ "audience_id": "aud_browsed_shoes", "is_member": true }
]
}The sweep is positives only. An audience the person is not in is absent from the list; there is no is_member: false row to read. So branch on presence — an id in the list means in, and an id missing from it means anything from “not a member” to “that audience cannot be answered for right now”.
An empty list is a normal answer, not an error. {"memberships": []} is what a person in nothing gets, and it is also what an identifier Zeotap has never seen gets. The endpoint deliberately does not tell the two apart: a membership key is published in a web page, so anyone can call it with an identifier they invented, and an invented one should learn nothing about the workspace.
When you need a definitive negative, name the audience. ?audience={id} reports that one audience’s verdict whatever it is, is_member: false included — you named it, so the answer discloses nothing you did not already know. That is the supported way to get a certain no, and it is what makes positives-only affordable on the sweep.
audience_id is the only field beside the verdict. It is what you branch on: it is stable, where a name can be edited at any time, and your integration has to hard-code it to branch on it in any case. Copy an audience’s id from its detail page, or from the copy icon beside its name in the audiences list.
The response carries no audience names. A name is the workspace’s own editorial label — “Lapsed high-value customers” — and this credential is readable by every visitor to the page that calls it, so publishing the catalogue’s names alongside the verdicts would disclose something the caller never asked to share. The rebuild times and possible-false-match notes you see beside each verdict in the Playground are likewise absent: they come from your signed-in session, not from a key.
Every response is served Cache-Control: no-store, refusals included. Membership is re-evaluated on each read and a revoked key must stop working immediately, so neither a verdict nor a refusal may be held anywhere between the endpoint and the page.
Which audiences appear
The sweep considers the workspace’s serveable realtime audiences and reports the person’s positives among them. An audience is left out of the sweep entirely — even for someone who would qualify — when it cannot be answered honestly, most often because its warehouse half has never been built, so every attribute condition in it would read as false and the verdict would be confidently wrong. Rather than serve that, the report omits the audience.
If an audience you expect is missing and you believe the person qualifies, check the Snapshots tab: an audience that has never been rebuilt has nothing to answer from. Asking for it directly with ?audience= returns a 422 that says so, which is the quickest way to separate “not a member” from “not serveable”.
Status codes
| Status | Meaning |
|---|---|
200 OK | Verdicts returned. An empty memberships array is a 200. |
400 Bad Request | The identifier index or value is missing. |
401 Unauthorized | Missing or malformed Authorization header, an invalid or revoked membership key, or a credential of the wrong class — an sk_, rk_, or svk_ key. |
403 Forbidden | Realtime audiences are not enabled for the key’s workspace. |
404 Not Found | The ?audience= id does not exist in the key’s workspace. |
422 Unprocessable Entity | The requested audience cannot be served: it is not an active realtime audience, it uses a condition the realtime evaluator does not support, or it has never been built. |
Freshness
The two halves of a verdict age differently, and it is worth designing for:
| Half of the verdict | How fresh |
|---|---|
| Live behaviour, from the event stream | Within about a second of the event arriving |
| Warehouse attributes, from the snapshot | As of the last rebuild — see Snapshots |
A verdict can also change back. Membership is re-evaluated on every read, and a late-arriving event can flip an answer either way, so read at the moment you need the answer rather than caching it.
“Has not done X” conditions can be over-inclusive early on. Zeotap starts tracking a condition when an audience first needs it, and answers from whatever it has rather than waiting out the full window. For a performed condition that just means someone qualifies as soon as they act. For a not performed condition, “not yet tracked” and “genuinely did not happen” look identical — so an audience that suppresses people who did something can briefly admit someone who did it. The verdict does not say which case it is. Give a newly created suppression audience its window before you rely on it.
Next Steps
- Playground — run this request against a real identifier.
- Snapshots — control the freshness of the warehouse half.
- Realtime Audiences — the audiences this endpoint reports on.
- Profile API — the attribute endpoint, for values rather than verdicts, and a server-side credential to match. Its
?include=audiencesreturns both in one call.