Braze Loader
The Braze loader pulls data out of Braze through the REST Export API — no Braze Currents subscription required. It brings your engagement platform’s user profiles (with custom attributes), segments, campaigns, Canvases, custom-event catalog, and daily KPIs into your data warehouse so you can model and activate them alongside the rest of your customer data.
Braze serves different objects in different ways, and this loader matches each one to how Braze actually exposes it:
- User profiles are exported asynchronously: Zeotap submits an export job for a segment, Braze produces a compressed archive of newline-delimited JSON, and the loader downloads and streams it.
- Everything else (segments, campaigns, Canvases, custom-event names, KPIs) is returned directly as JSON from a
GETendpoint.
Prerequisites
- A Braze account with REST API access.
- A Braze REST API key with the export permissions for the streams you want (see Authentication).
- Your instance-specific REST endpoint (base URL).
- A connected Warehouse with write permissions on the target schema.
Authentication
The Braze loader authenticates with a REST API key sent as a Bearer token (Authorization: Bearer <key>).
Creating a REST API key
- In the Braze dashboard, go to Settings > APIs and Identifiers > REST API Keys.
- Click Create API Key.
- Grant the permissions for the streams you plan to sync:
users.export.segment— User Profilessegments.list— Segmentscampaigns.list— Campaignscanvas.list— Canvasesevents.list— Custom Events (catalog)kpi.dau.data_series— KPI — Daily Active Users
- Save the key and paste it into the REST API Key field in Zeotap.
Finding your REST endpoint (base URL)
Braze is multi-instance — your account lives on one instance and you must call the matching REST endpoint. Find it on the same APIs and Identifiers page. Common values:
| Instance | REST Endpoint |
|---|---|
| US-01 | https://rest.iad-01.braze.com |
| US-02 … US-08 | https://rest.iad-0N.braze.com |
| US-10 | https://rest.us-10.braze.com |
| EU-01 | https://rest.fra-01.braze.eu |
| EU-02 | https://rest.fra-02.braze.eu |
| AU-01 | https://rest.au-01.braze.com |
Paste the full URL (including https://) into the REST Endpoint (Base URL) field.
Configuration
| Setting | Description | Default |
|---|---|---|
| REST API Key | Braze REST API key, sent as a Bearer token | — (required) |
| REST Endpoint (Cluster) | Your instance’s Braze cluster, chosen from a dropdown (e.g. US-01 → https://rest.iad-01.braze.com). Find your cluster under Settings → APIs and Identifiers | — (required) |
| Segment | The segment to export in the User Profiles stream. Once your REST endpoint and API key are set, pick a segment by name from the dropdown (loaded live from Braze), or paste a segment API Identifier directly. Required only when User Profiles is enabled | — |
| User Profile Fields | Which profile fields to export (User Profiles stream), picked from a checklist. Defaults include all custom attributes | Default set (below) |
| Custom Attributes to Export | Specific custom-attribute names to include in the User Profiles export, each entered as a tag (e.g. loyalty_tier, last_purchase_category). Leave empty to export all custom attributes when custom_attributes is in the field set. Applies only to the User Profiles stream | — (all) |
| User Export Max Wait (minutes) | How long to poll for the asynchronous User Profiles segment export to become ready before giving up. Large segment exports can take an hour or more, so raise this for very large segments. Applies only to the User Profiles stream | 60 |
| KPI Lookback (days) | How many days of the Daily Active Users series to pull each run (1–100) | 30 |
The default User Profile Fields set includes identity fields, subscription status, total_revenue, all custom_attributes, and the 90-day engagement arrays (custom_events, purchases, campaigns_received, canvases_received), plus devices, push_tokens, apps, and user_aliases.
Available Objects
| Object | API Name | How it’s fetched | Default Sync Mode |
|---|---|---|---|
| User Profiles | user_profiles | POST /users/export/segment → async ZIP download | Full refresh |
| Segments | segments | GET /segments/list (JSON) | Full refresh |
| Campaigns | campaigns | GET /campaigns/list (JSON) | Incremental |
| Canvases | canvases | GET /canvas/list (JSON) | Incremental |
| Custom Events (Catalog) | custom_events | GET /events/list (JSON) | Full refresh |
| KPI — Daily Active Users | kpi_dau | GET /kpi/dau/data_series (JSON) | Full refresh |
Purchases are intentionally not a standalone stream. Per-user purchase summaries (last 90 days) are already included in the
user_profilesstream’spurchasescolumn.
Key Columns
User Profiles (user_profiles) flattens each profile: identity fields (external_id, braze_id, email, phone, first_name, last_name, …) and total_revenue become typed top-level columns, while nested structures — custom_attributes, custom_events, purchases, devices, push_tokens, apps, user_aliases, campaigns_received, canvases_received, last_coordinates — are stored as JSON. A raw column preserves the complete original record so nothing is lost.
| Stream | Notable columns | Incremental cursor |
|---|---|---|
user_profiles | external_id, braze_id, email, total_revenue, custom_attributes (json), custom_events (json), purchases (json), raw (json) | — (full refresh) |
segments | id, name, analytics_tracking_enabled (bool), tags (json) | — |
campaigns | id, name, last_edited (datetime), is_api_campaign (bool), tags (json) | last_edited |
canvases | id, name, last_edited (datetime), tags (json) | last_edited |
custom_events | event_name | — |
kpi_dau | time (date), dau (integer) | — |
Sync Modes
- Incremental —
campaignsandcanvasessupport incremental sync onlast_edited. Zeotap saves the highestlast_editedit has seen and passes it back as thelast_edit.time[gt]filter on the next run, so only campaigns/Canvases edited since the last sync are pulled. - Full refresh —
user_profiles,segments,custom_events, andkpi_daure-read their full result each run. The segment export has no server-side incremental filter, and the segment/event catalogs and KPI window are small enough to re-pull cheaply.
Data Window
Within an exported user profile, the custom_events, purchases, campaigns_received, and canvases_received arrays reflect only the last 90 days of activity (this is a Braze API limitation). The first and count sub-fields on custom_events/purchases are all-time, however. The kpi_dau stream is bounded by the configurable KPI Lookback (1–100 days).
How It Works
- Async user-profile export — Zeotap
POSTs an export job for the configured segment with your chosenfields_to_export. Braze responds with a downloadurl. Because a large export takes time to generate (an app with 20M users can take an hour or more), the loader polls that URL — a403/404means “not ready yet” — and downloads the ZIP once it is available. Braze generates roughly one file per 5,000 users; the loader reads every file in the archive. - Streaming, not buffering — The downloaded archive is written to a temporary file on disk, then each entry is decompressed and read line by line, emitting rows in batches. The full result set is never held in memory, so even multi-gigabyte exports stay within the job’s memory budget.
- Page-based JSON — The list endpoints (
segments,campaigns,canvases,custom_events) page through results with a 0-basedpageparameter (100 items per page; 250 for events), stopping when a short page is returned. - Cursor checkpointing — For
campaigns/canvases, the loader tracks the maximumlast_editedand checkpoints it after each batch; Zeotap persists it between runs.
Cloud-storage exports
If you have configured your own cloud-storage credentials (S3, Azure, or Google Cloud Storage) inside Braze, Braze writes segment exports directly to your bucket and the API response contains no download url. In that case the User Profiles stream cannot pull the data over the API — load it instead with the corresponding Zeotap cloud-storage loader (Amazon S3, Google Cloud Storage, or Azure Blob). The loader surfaces a clear error explaining this when it sees an empty url.
Rate Limits
Braze applies a default rate limit of 250,000 requests per hour to most export endpoints. Exceptions relevant to this loader:
GET /events/listshares a limit of 1,000 requests per hour with a few other catalog endpoints.- Only one user-profile export per segment may run at a time; a second concurrent export of the same segment returns
429.
The loader is designed to stay well within these limits — the list endpoints paginate rather than hammer, and the user export submits a single job per run.
Troubleshooting
| Issue | Solution |
|---|---|
| ”authentication failed (HTTP 401)“ | The REST API key is invalid, revoked, or missing. Recreate it under Settings > APIs and Identifiers > REST API Keys |
| ”forbidden (HTTP 403)” on a stream | The API key is valid but lacks that endpoint’s permission. Grant the permission listed under Authentication for each stream you sync |
| ”API connectivity failed (check the base URL)“ | The REST Endpoint is wrong for your instance. Confirm it on the APIs and Identifiers page — note US-10/AU/ID/JP/KR use rest.<region>-01, while US-01–08 use rest.iad-0N |
| ”segment_id is required for the user_profiles stream” | Set the Segment ID in configuration. Find IDs via the segments stream or the API Keys page |
| ”user export returned no download URL” | Braze wrote the export directly to your configured cloud-storage bucket. Use the S3/GCS/Azure loader instead (see Cloud-storage exports) |
| “export did not become ready within …” | A very large export exceeded the poll window (the configured User Export Max Wait, default 60m). Raise export_poll_max_wait_minutes, reduce fields_to_export, or split the segment into smaller segments (e.g. by a random bucket number) |
| “rate limited (HTTP 429)“ | Another export for the same segment is already running, or you hit the hourly limit. Wait and retry, or sync less frequently |
| Custom event data is missing | The custom_events stream is the catalog of event names only. Per-user custom-event summaries (last 90 days) live in the custom_events JSON column of the user_profiles stream |
| Some profile fields are empty | Only the fields listed in User Profile Fields are exported, and only fields that have values on a given user appear. Add fields to the list to widen the export |
Next Steps
- Create a model to transform raw Braze data — flatten
custom_attributesJSON, or joincampaignsto campaign KPIs - Build audiences on engagement (
total_revenue, custom-event recency) and sync them back to Braze or other tools