Pinterest API
Ruby wrapper for the Pinterest REST API v5.
Installation
Add to your Gemfile:
gem "pinterest-ads", git: "https://github.com/stitchfix/pinterest-api"
Then run:
bundle install
Configuration
Global configuration
Pinterest.configure do |c|
c.client_id = ENV["PINTEREST_CLIENT_ID"]
c.client_secret = ENV["PINTEREST_CLIENT_SECRET"]
c.access_token = ENV["PINTEREST_ACCESS_TOKEN"]
c.redirect_uri = ENV["PINTEREST_REDIRECT_URI"]
end
Per-client configuration
You can also pass credentials directly when instantiating a client. Per-client values take precedence over the global configuration.
client = Pinterest::Client.new(
client_id: "your_app_id",
client_secret: "your_app_secret",
access_token: "your_bearer_token",
redirect_uri: "https://example.com/callback"
)
Optional settings
| Option | Default | Description |
|---|---|---|
base_url |
https://api.pinterest.com/v5 |
API base URL |
auth_url |
https://www.pinterest.com/oauth/ |
OAuth authorization URL |
timeout |
30 |
Request timeout in seconds |
open_timeout |
10 |
Connection open timeout in seconds |
These can be set via Pinterest.configure or passed as keyword arguments to Pinterest::Client.new.
Usage
OAuth
1. Generate the authorization URL
url = client.oauth.(
redirect_uri: "https://example.com/callback",
scope: %w[ads:read ads:write],
state: SecureRandom.hex(16)
)
# Redirect the user to this URL
2. Exchange the authorization code for tokens
tokens = client.oauth.exchange_code(
code: params[:code],
redirect_uri: "https://example.com/callback"
)
# tokens => { "access_token" => "...", "refresh_token" => "...", "expires_in" => 86400, ... }
3. Refresh an access token
tokens = client.oauth.refresh(refresh_token: "your_refresh_token")
client.access_token = tokens["access_token"]
4. Revoke a token
client.oauth.revoke(token: "token_to_revoke", token_type_hint: "access_token")
5. Generate a conversion API token
result = client.oauth.conversion_token
# result => { "access_token" => "...", "token_type" => "conversion" }
Audiences
All audience methods require an ad_account_id.
# List audiences
audiences = client.audiences.list(ad_account_id: "123456")
# audiences => { "items" => [...], "bookmark" => "..." }
# Create an audience
audience = client.audiences.create(
ad_account_id: "123456",
name: "My Audience",
audience_type: "CUSTOMER_LIST",
rule: { ... }
)
# Get a specific audience
audience = client.audiences.find(ad_account_id: "123456", audience_id: "789")
# Update an audience
client.audiences.update(ad_account_id: "123456", audience_id: "789", name: "New Name")
Customer Lists
# List customer lists
lists = client.customer_lists.list(ad_account_id: "123456")
# Create a customer list
list = client.customer_lists.create(
ad_account_id: "123456",
name: "Email Subscribers",
list_type: "EMAIL",
records: "user1@example.com\nuser2@example.com"
)
# Get a specific customer list
list = client.customer_lists.find(ad_account_id: "123456", customer_list_id: "789")
# Add or remove records
client.customer_lists.update(
ad_account_id: "123456",
customer_list_id: "789",
operation_type: "ADD",
records: "user3@example.com"
)
Customer List Uploads (Multipart S3)
For large customer lists, use the multipart upload workflow:
# 1. Create the upload (returns presigned S3 URLs)
upload = client.customer_list_uploads.create(
ad_account_id: "123456",
customer_list_id: "789",
operation: "ADD",
total_parts: 3
)
# upload => { "customer_list_upload" => {...}, "s3_multipart_upload_data" => {...} }
# 2. Upload parts to S3 using the presigned URLs (outside this gem)
# 3. Trigger processing
client.customer_list_uploads.run(
ad_account_id: "123456",
customer_list_id: "789",
customer_list_upload_id: upload.dig("customer_list_upload", "id")
)
# Check upload status
status = client.customer_list_uploads.find(
ad_account_id: "123456",
customer_list_id: "789",
customer_list_upload_id: "upload_id"
)
Pagination
List endpoints return a bookmark value for cursor-based pagination:
all_audiences = []
bookmark = nil
loop do
result = client.audiences.list(ad_account_id: "123456", bookmark: bookmark, page_size: 25)
all_audiences.concat(result["items"])
bookmark = result["bookmark"]
break if bookmark.nil?
end
Error Handling
The gem raises typed exceptions for HTTP errors:
| Status | Exception |
|---|---|
| 400 | Pinterest::BadRequestError |
| 401 | Pinterest::AuthenticationError |
| 403 | Pinterest::ForbiddenError |
| 404 | Pinterest::NotFoundError |
| 429 | Pinterest::RateLimitError |
| 5xx | Pinterest::ServerError |
All exceptions inherit from Pinterest::Error and expose status, code, and response attributes.
begin
client.audiences.find(ad_account_id: "123", audience_id: "bad_id")
rescue Pinterest::NotFoundError => e
puts e. # API error message
puts e.status # 404
rescue Pinterest::Error => e
puts "Unexpected error: #{e.} (HTTP #{e.status})"
end
Requirements
- Ruby >= 3.0.0
License
MIT