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.authorization_url(
  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.message # API error message
  puts e.status  # 404
rescue Pinterest::Error => e
  puts "Unexpected error: #{e.message} (HTTP #{e.status})"
end

Requirements

  • Ruby >= 3.0.0

License

MIT