Installation

Official clients are published for six languages. Each exposes the same resources and method names as this reference, so content.list is client.content.list() in every one of them.

  1. Install the client

    pip install zelf-sdk
  2. Set your key

    export ZELF_API_KEY="<your-api-key>"

    A key is issued per brand. Create one in the dashboard, then keep it out of the code you commit.

  3. Make a call

    Python
    import os
    
    from zelf import ZelfAPIClient
    
    client = ZelfAPIClient(api_key=os.environ["ZELF_API_KEY"])
    
    result = client.content.list(
        search="running shoes",
        posted_within_days=30,
    )
    print(result)

    That returns the posts matching the search, newest first, as one page of the standard envelope. Every filter the search takes is on the Content page.

Client behaviour

A client reads the key from ZELF_API_KEY and the base URL from ZELF_BASE_URL when neither is passed. It walks pagination, retries safe requests, verifies webhook signatures, and raises a typed error carrying the log_id.

Source and per-language documentation: github.com/hellozelf/zelf-sdk

Pagination

A list call returns one page. Walking the rest takes one of three bounds: everything, at most N pages, or at most N items. The item bound never fetches a page it does not need, and the envelope stays on the page itself: item_count, page, applied_filters, log_id.

Python
for item in page.auto_paging(max_pages=5):
    print(item.id)

Typed errors

Every failure is a typed error carrying the status, the error code and the log_id to quote in a support request. The type follows the status, so a retry decision never has to parse a message. A network problem raises a connection or timeout error instead.

Python
try:
    client.content.details("con_nope")
except zelf.NotFoundError as error:
    print(error.status_code, error.code, error.log_id)
  • 400BadRequestErrorVALIDATION_ERRORINVALID_IDBRAND_ID_REQUIRED
  • 401AuthenticationErrorINVALID_API_KEYEXPIRED_API_KEYREVOKED_API_KEY
  • 403PermissionDeniedErrorPERMISSION_DENIED
  • 404NotFoundErrorNOT_FOUND
  • 406NotAcceptableErrorNOT_ACCEPTABLE
  • 409ConflictErrorA conflicting write
  • 429RateLimitErrorRATE_LIMIT_EXCEEDED
  • 500InternalServerErrorINTERNAL_ERROR
  • 503ServiceUnavailableErrorSERVICE_UNAVAILABLE
  • no replyZelfConnectionError, ZelfTimeoutErrorThe request never reached the API, or timed out

Retries

A failed GET is retried twice by default, honouring Retry-After and otherwise backing off exponentially with jitter, capped at 8 seconds. Writes are never retried: the API publishes no idempotency key, so a retried detection.create would start a second billable run. Opt in per client if your call site can tolerate it.

Python
client = ZelfAPIClient(max_retries=5, retry_writes=True)

Webhook verification

parse checks the signature and returns a typed event. Verification uses each language's constant-time comparison, enforces a 300 second timestamp window against replays, and accepts a list of secrets so a rotation does not drop deliveries. For a manifest delivery, rows downloads the NDJSON and verifies the published SHA-256 over each body before yielding anything.

Python
from zelf import webhooks

event = webhooks.parse(request.body, request.headers, secret)
if event.event_type is webhooks.EventType.CONTENT_SNAPSHOT:
    for row in event.items:
        print(row["id"])

Async

Python ships a second client with the same surface.

Python
import asyncio

from zelf import ZelfAPIClientAsync


async def main():
    async with ZelfAPIClientAsync() as client:
        page = await client.content.list(search="running shoes")
        print(page.item_count)


asyncio.run(main())