Track users, send messages, export data, and more
This is Blinkist's fork of braze-inc/braze-api-client-ruby, which has had no commit since January 2022 and is still published as 0.1.1. We maintain it here because we need endpoints upstream never shipped. The module name (
BrazeClient) and the public method surface are unchanged, so switching a Gemfile to this fork requires no call-site changes.Changes on top of upstream 0.1.1:
merge_users—POST /users/mergeexport_user_ids—POST /users/export/idsBrazeClient::RateLimitErrorraised on HTTP 429 (subclass ofApiError, so existing rescues still catch it)ArgumentErrorraised before the HTTP call whenmerge_usersorexport_user_idsexceeds a documented batch cap (opt out withconfig.client_side_validation = false)- Ruby >= 3.4
This fork is not published to RubyGems. Add it to your Gemfile from git:
gem "braze_api_client", git: "https://github.com/blinkist/braze-api-client-ruby.git", tag: "v0.2.0"Followed by running:
bundle installGenerate an API key with permissions for the endpoints you need to call.
You can determine which API host to use based on your Braze dashboard URL:
| Instance | URL | API Host |
|---|---|---|
| US-01 | https://dashboard-01.braze.com |
rest.iad-01.braze.com |
| US-02 | https://dashboard-02.braze.com |
rest.iad-02.braze.com |
| US-03 | https://dashboard-03.braze.com |
rest.iad-03.braze.com |
| US-04 | https://dashboard-04.braze.com |
rest.iad-04.braze.com |
| US-05 | https://dashboard-05.braze.com |
rest.iad-05.braze.com |
| US-06 | https://dashboard-06.braze.com |
rest.iad-06.braze.com |
| US-08 | https://dashboard-08.braze.com |
rest.iad-08.braze.com |
| EU-01 | https://dashboard-01.braze.eu |
rest.fra-01.braze.eu |
| EU-02 | https://dashboard-02.braze.eu |
rest.fra-02.braze.eu |
# Load the gem
require 'braze_api_client'
# Setup authorization
BrazeClient.configure do |config|
config.access_token = 'YOUR_API_KEY'
config.server_variables[:host] = 'rest.YOUR-REGION.braze.com'
endYou can use the /users/track endpoint to record custom events, purchases, and update user profile attributes. Braze accepts at most 75 objects combined across attributes, events and purchases per request (legacy contracts get 75 of each). This is not enforced client-side — Braze rejects an oversized batch itself.
braze_api = BrazeClient::RestApi.new
begin
result = braze_api.track_users({
:events => [{
:external_id => "user123",
:app_id => "yourappid",
:time => Time.now,
:name => "watched_trailer",
}],
:attributes => [{
:external_id => "user456",
:first_name => "Alice",
:favorite_color => "blue",
}],
:purchases => [
:external_id => "user456",
:app_id => "yourappid",
:time => Time.now,
:product_id => "product_name",
:currency => "USD",
:price => 12.12,
:quantity => 2,
:properties => {
:color => "blue",
}
]
})
p result
rescue BrazeClient::ApiError => e
puts "Exception when calling RestApi->users_track: #{e}"
endPOST /users/merge merges one profile into another. Up to 50 merges per request, and the call is
asynchronous — a 202 only means Braze accepted the batch, so confirm the result with
export_user_ids rather than trusting the status code.
braze_api = BrazeClient::RestApi.new
begin
result = braze_api.merge_users({
:merge_updates => [{
:identifier_to_merge => { :external_id => "old_user_id" },
:identifier_to_keep => { :external_id => "surviving_user_id" },
}]
})
p result
rescue BrazeClient::RateLimitError => e
puts "Rate limited by Braze, back off and retry: #{e.code}"
rescue BrazeClient::ApiError => e
puts "Exception when calling RestApi->users_merge: #{e}"
endMerging by email or phone instead of external_id additionally requires a prioritization
array — see the endpoint docs.
POST /users/export/ids returns the current state of up to 50 profiles per identifier type. fields_to_export is
required for workspaces onboarded on or after 2024-08-22 and also unlocks a higher rate limit.
The response is returned as a plain Hash because the exported fields are workspace-specific.
braze_api = BrazeClient::RestApi.new
begin
result = braze_api.export_user_ids({
:external_ids => ["user123"],
:fields_to_export => ["external_id", "email", "custom_attributes"],
})
p result[:users]
p result[:invalid_user_ids]
rescue BrazeClient::ApiError => e
puts "Exception when calling RestApi->users_export_ids: #{e}"
endbraze_api = BrazeClient::RestApi.new
begin
result = braze_api.delete_users({
# use any or all of these 3 identifier types
:external_ids => ["user1", "user2"],
:user_aliases => ["alias123", "alias456"],
:braze_ids => ["braze_identifier1", "braze_identifier2"],
})
p result
rescue BrazeClient::ApiError => e
puts "Exception when calling RestApi->users_track: #{e}"
endbraze_api = BrazeClient::RestApi.new
begin
result = braze_api.new_user_aliases({
:user_aliases => [{
:external_id => "user123", # optional
:alias_name => "c123",
:alias_label => "customer_id",
}]
})
p result
rescue BrazeClient::ApiError => e
puts "Exception when calling RestApi->users_track: #{e}"
endbraze_api = BrazeClient::RestApi.new
begin
result = braze_api.identify_users({
:aliases_to_identify => [{
:external_id => "user123",
:user_alias => {
:alias_name => "c123",
:alias_label => "customer_id",
}
}],
})
p result
rescue BrazeClient::ApiError => e
puts "Exception when calling RestApi->users_track: #{e}"
endbraze_api = BrazeClient::RestApi.new
begin
result = braze_api.rename_external_ids({
:external_id_renames => [{
:current_external_id => "user123",
:new_external_id => "u123",
}],
})
p result
rescue BrazeClient::ApiError => e
puts "Exception when calling RestApi->users_track: #{e}"
endbraze_api = BrazeClient::RestApi.new
begin
result = braze_api.remove_external_ids({
:external_ids => ["user123", "user456"],
})
p result
rescue BrazeClient::ApiError => e
puts "Exception when calling RestApi->users_track: #{e}"
endOnly /users/merge and /users/export/ids enforce their cap client-side, and both are new in this fork
so nothing can regress. Everything else is documented here but not checked. To bypass a guard, set
config.client_side_validation = false — note that also disables the nil-body checks.
Braze limits per endpoint group, not globally (reference). The numbers that constrain callers of this gem:
| Endpoint | Batch cap | Rate limit |
|---|---|---|
/users/track |
75 objects combined across the three arrays | 3,000 requests / 3 s burst |
/users/merge |
50 merge_updates |
20,000 / min, shared with delete, alias/new, alias/update, identify |
/users/delete |
50 identifiers, one identifier type per request | shared 20,000 / min pool |
/users/export/ids |
50 external_ids and 50 user_aliases, counted separately |
250 / min (workspaces from 2024-08-22), else 2,500 / min |
/users/alias/new |
50 aliases | shared 20,000 / min pool |
/users/identify |
50 aliases | shared 20,000 / min pool |
/users/external_ids/rename |
50 rename objects | 1,000 / min |
/users/external_ids/remove |
50 external IDs | 1,000 / min |
A 429 raises BrazeClient::RateLimitError. This gem intentionally has no retry loop — back off in
the calling application (in a Sidekiq worker, let the job retry).
Several endpoints report partial failure inside a 2xx body, so a successful call still needs its
response inspected: errors on /users/track, rename_errors on /users/external_ids/rename,
removal_errors on /users/external_ids/remove, invalid_user_ids on /users/export/ids.
Upstream is generated from a private codegen project, so changes here are handwritten in the
generated style. Keep the BrazeClient public surface backwards compatible — blinkist-job-system,
blinkist-web and blinkist-organisations all depend on it.
- API version: 0.1.1 (upstream OpenAPI document)
- Package version: 0.2.0
- Build package: org.openapitools.codegen.languages.RubyClientCodegen