Embedded Sponsorship API
Overview
The Embedded Sponsorship API allows partners to integrate ZipRecruiter's job sponsorship capabilities directly into their ATS. Partners can generate access tokens for their users, check sponsorship status of jobs, and embed sponsorship flows in their UI.
6 Steps to Get Started
Get a sponsorship button working in your ATS in six easy steps:
- Get credentials — Receive a Job API Key and a Sponsorship API Key (a single
Authorization: Basiccredential) from your ZipRecruiter partner contact. Also provide all domains where you will embed the SDK (including dev, staging, and production) so ZipRecruiter can whitelist them. The iframe will not load until your domains are whitelisted. - Post a job — Use the Jobs API to create a job listing. A job must exist before it can be sponsored.
- Map user roles — Decide which roles to assign each ATS user type (e.g., admin gets
can-sponsor,can-view-all-jobs,can-unsponsor,can-manage-billing; recruiter getscan-sponsor,can-view-all-jobs,can-unsponsor). - Exchange credentials for an access token — Your server calls
POST /ats-embed/v0/api/tokenwith Basic Auth to get a short-lived access token. - Include the SDK and annotate job elements — Add the Sponsorship SDK script tag to your ATS page with the access token, and add
data-zr-job-idattributes to each job element. - See the button — The SDK automatically renders a "Sponsor on ZipRecruiter" button inside each annotated element. On completion, it updates to "Pause ZipRecruiter Sponsorship."
For detailed SDK setup instructions, see the Sponsorship SDK guide.
Authentication
The Sponsorship API uses Basic authentication to allow access to all endpoints. You will be provided an API key separate from your Jobs API key; please keep it secret.
If your client's Basic Auth library requires separate username and
password fields, simply decode the key we provide.
Example authentication header
curl https://api.ziprecruiter.com/ats-embed/v0/api/token \
-H "Authorization: Basic YOUR_SPONSORSHIP_API_KEY" ...
Operations
Token Exchange
POST /ats-embed/v0/api/token
Exchange Sponsorship API credentials for an access token.
Example request
curl -X POST https://api.ziprecruiter.com/ats-embed/v0/api/token \
-H "Authorization: Basic YOUR_SPONSORSHIP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "hiring@acme.com",
"externalEmployerId": "emp-456",
"atsUserId": "user-789",
"roles": ["can-sponsor", "can-view-all-jobs", "can-manage-billing"]
}'
Example response
{
"access_token": "eyJhbGciOiJkaXIiLCJlbmMiOi...",
"expires_in": 7200,
"user": {
"email": "hiring@acme.com",
"state": "returning"
}
}
Request Headers
| Header | Description |
|---|---|
Authorization | Basic YOUR_SPONSORSHIP_API_KEY |
Content-Type | application/json |
Request Body
Request body fields
| Field | Required | Type | Description |
|---|---|---|---|
email | Yes | string | User's email address. |
externalEmployerId | Yes | string | Unique employer identifier in your system. |
facilityId | No | string | Optional finer-grained identifier under externalEmployerId — see Facilities below. |
atsUserId | Yes | string | User's unique identifier in your ATS. |
roles | No | string[] | Permissions for this user (see Roles). If omitted or empty, the user has no permissions. |
companyName | No | string | Employer company name (pre-fills registration). |
website | No | string | Company website URL (pre-fills registration). |
zip | No | string | Company zip code (pre-fills registration). |
fullName | No | string | User's full name (pre-fills registration). |
title | No | string | User's job title (pre-fills registration). |
phone | No | string | User's phone number (pre-fills registration). |
Response Body
Response body fields
| Field | Required | Type | Description |
|---|---|---|---|
access_token | Yes | string | Bearer token (JWT) for subsequent API calls. |
expires_in | Yes | integer | Token lifetime in seconds (7200 = 2 hours). |
user | Yes | object | User identity and account state. |
user.email | Yes | string | The email address from the request. |
user.state | Yes | string | Account state: "returning", "new", "verify-identity", or "no-access". |
Facilities
If your externalEmployerId represents a parent account that covers multiple
locations or sub-brands (for example, a franchise with several facilities),
pass an optional facilityId on the token exchange request to keep each
facility's billing separate.
- Users who share the same
externalEmployerIdand the samefacilityId(including users who never send afacilityId) end up in the same ZipRecruiter organization, with one shared payment method and plan. - The first
facilityIdseen for a givenexternalEmployerIdestablishes a new, independent ZipRecruiter organization for that facility. Every subsequent request with the sameexternalEmployerId+facilityIdpair routes new users into that same organization. - Omitting
facilityIdis equivalent to using one fixed default facility for that employer — existing integrations that never send it see no change. - This affects new users only: an ATS user who already has a ZipRecruiter
account continues to use their own existing organization, regardless of
facilityId.
Sponsored Status
POST /ats-embed/v0/api/sponsored_status
Determine which jobs are currently sponsored.
Example request
curl -X POST "https://api.ziprecruiter.com/ats-embed/v0/api/sponsored_status" \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "jobIds": ["job-010", "job-011"] }'
Example response
{
"success": true,
"statuses": {
"job-010": "sponsored",
"job-011": "unsponsored"
}
}
Request Headers
| Header | Description |
|---|---|
Authorization | Bearer ACCESS_TOKEN |
Content-Type | application/json |
Request Body
| Field | Required | Type | Description |
|---|---|---|---|
jobIds | Yes | string[] | Array of external job IDs (max 50). |
Embed Flow URLs
The sponsorship widget is loaded in an iframe using the URLs below. Pass the external job ID and the access token obtained from the token exchange.
Embed flow URLs and parameters
| Flow | URL | Description |
|---|---|---|
| Sponsor | /ats-embed/v0/?jobId=<id>&token=<token> | Select jobs, choose distribution level, enter payment. |
| Manage | /ats-embed/v0/manage?jobId=<id>&token=<token> | View and manage sponsored jobs. |
| Parameter | Description |
|---|---|
jobId | External job ID. |
token | Access token from the token exchange. |
Roles
Roles control what a user can do inside the sponsorship widget. Assign roles per user when
generating their access token (see the roles parameter in Token Exchange).
Available roles
| Role | What It Controls |
|---|---|
can-sponsor | Required to sponsor jobs. Without this role, the user sees a "no access" screen. |
can-view-all-jobs | With this role, the user sees all company jobs on the job selection screen. Without it, they see only their own. |
can-unsponsor | Required to stop sponsorship on a job. |
can-manage-billing | Required to add or change the payment method. Without it, the user can still sponsor using an existing card on file but cannot replace it. |
If roles is omitted or empty, the user has no permissions
and cannot perform any gated actions. Roles are enforced server-side. The widget displays
appropriate messages for each permission state, but the API independently rejects
unauthorized requests.
Typical Role Assignments
Typical role assignments by user type
| User Type | Suggested Roles |
|---|---|
| Admin / hiring manager | can-sponsor, can-view-all-jobs, can-unsponsor, can-manage-billing |
| Recruiter | can-sponsor, can-view-all-jobs, can-unsponsor |
| Hiring team member | can-sponsor |
Error Responses
All Sponsorship API responses include a success boolean at the top level. On failure,
success is false and an error object is included:
Example error response
{
"success": false,
"error": {
"code": "INVALID_TOKEN",
"message": "The access token is invalid or expired."
}
}
Error codes
| Code | Status | Description |
|---|---|---|
UNAUTHENTICATED | 401 | Missing or invalid authorization. |
INVALID_TOKEN | 401 | Bearer token is invalid or expired. |
INSUFFICIENT_PERMISSIONS | 400 | User lacks a required role (see Roles). |
MISSING_EMAIL | 400 | email field is missing or empty. |
INVALID_EMAIL | 400 | email is not a valid email address. |
MISSING_EMPLOYER_ID | 400 | externalEmployerId field is missing or empty. |
INVALID_REQUEST | 400 | Request body is missing required fields or contains invalid values. |
RATE_LIMITED | 429 | Too many requests; retry after the period indicated by Retry-After. |
INTERNAL_ERROR | 500 | An unexpected error occurred. |
Rate Limits
All Sponsorship API endpoints are rate-limited. When a limit is exceeded the API returns
HTTP 429 with a Retry-After header.
Rate limit response headers
Response Headers
The following headers are included on every Sponsorship API response:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the current window. |
X-RateLimit-Remaining | Requests remaining in the current window. |
Retry-After | Seconds until the window resets (only on 429 responses). |
Token endpoint limits
Token Endpoint Limits
POST /ats-embed/v0/api/token is subject to two independent limits, both keyed
on client_id. A request is rejected if either limit is exceeded.
| Scope | Limit | Window | Notes |
|---|---|---|---|
Per client_id | 3,000 requests | 1 minute | Burst protection. Cache tokens and refresh before expiry. |
Per client_id | 100,000 requests | 1 hour | Overall budget across all employers for a single partner. |
Sponsored status endpoint limits
Sponsored Status Endpoint Limits
POST /ats-embed/v0/api/sponsored_status is rate-limited per user.
| Limit | Window |
|---|---|
| 60 requests | 1 minute |