NAV
go java python

ZipRecruiter Partner Platform

Welcome to the ZipRecruiter Partner Program.

If you have any questions or concerns regarding the program or our documentation, please contact us or find more information at the following:

Web: https://www.ziprecruiter.com/ats-partners
Email: atsintegrations@ziprecruiter.com

Job Ingestion

Candidate Delivery

Disposition Feedback

Job API

The Job API is used by ZipRecruiter partners to post jobs to our network, and later, to close them.

One endpoint, https://api.ziprecruiter.com/partner/v0/job can be used for basic CRUD operations:

The body of the request must contain a json representation of a job, described below.

Unicode in job_ids

In the Job API's GET, PUT, and DELETE operations, a job_id is included in the URI.
If this job_id has any characters that are not in the unreserved set, they must be UTF-8 encoded, then the bytes URL-encoded (percent-encoded) Wikipedia. (curl does this automatically, perhaps your library of choice does as well.)

Authentication

Sample authorization. Make sure to replace meowmeowmeow in all examples with your API key.

# With shell, you can just pass the correct header with each request
curl https://api.ziprecruiter.com/partner/v0/job \
  -H "Authorization: Basic meowmeowmeow" ...

Partner API uses Basic authentication to allow access to all endpoints. You will be provided an API key; please keep it secret.

Job API Operations

Post a job

The job is an json-encoded object in the content. The job model is described below. The job (identified by job_id) cannot already exist.

HTTP Request

POST https://api.ziprecruiter.com/partner/v0/job

curl https://api.ziprecruiter.com/partner/v0/job \
  -H "Authorization: Basic meowmeowmeow" \
  -X POST \
  -H "Content-Type: application/json" \
  -d @job.json

Parameters

The job is an json-encoded object in the content. The job model is described below.

Update a job

The job is an json-encoded object in the content. The job model is described below. The job (identified its job_id in the body) must already exist.

Note: updating a job always makes it active again, if it was previously closed.

HTTP Request

Either:

If the latter url is used, the JOB_ID in the url must match the one in the body.

curl https://api.ziprecruiter.com/partner/v0/job \
  -H "Authorization: Basic meowmeowmeow" \
  -X PUT \
  -H "Content-Type: application/json" \
  -d @job.json

Get a specific job

This endpoint retrieves a specific job by id.
At present, this only serves to demonstrate that the job exists in our system.

curl "https://api.ziprecruiter.com/partner/v0/job/emlwcmVjcnVpdGVy-3215" \
  -H "Authorization: Basic meowmeowmeow"

HTTP Request

GET https://api.ziprecruiter.com/partner/v0/job/<JOB_ID>

URL Parameters

Parameter Description
JOB_ID The ID of the job to retrieve.

Close a Job

curl "https://api.ziprecruiter.com/partner/v0/job/emlwcmVjcnVpdGVy-3215" \
  -H "Authorization: Basic meowmeowmeow" \
  -X DELETE

This request closes a specific job.

HTTP Request

DELETE https://api.ziprecruiter.com/partner/v0/job/<JOB_ID>

URL Parameters

Parameter Description
JOB_ID The ID of the job to close

Re-open a Job

To re-open a job, simply update it again.
(It is fine to PUT with exactly the same fields.)

The Job Model

The job is represented as a hash (mapping, dictionary) of fields, encoded as JSON in the request or response body.
Fields are optional unless otherwise specified.

Do not include fields that have no value, when you create a new job (POST). You may give a JSON null value for a field you wish to erase (such as apply_url), in an update (PUT).

The job_id and employer_id fields are required, and must be unique in the partner's system. The employer_name should be given the first time a job is created by that employer, it's optional after that.

A job location is required. You must always give country, and then either:

If you have more than one of the above, provide all that you have. (For remote jobs, you can give the location of the hiring company's headquarters.)

The job title is required.

A job_type is required; it can be one of specific set of values (an enum), see below.

A minimal sample job, in json:

{
   "employer_id": "emlwcGFydG5lcg-042",
   "employer_name": "Stenographica, Ltd",
   "job_id": "emlwcmVjcnVpdGVy-3215",
   "title": "Longhand",
   "job_type": "contract",
   "state": "NY",
   "city": "New York",
   "country": "US",
   "description": "<div>We need someone to write things by hand, on paper, legibly, in cursive.</div>"
}

The basics are

Other fields:

Job API Response

The response to a successful request is a json object, as the example shown at right.

{
  "job": {
    "job_id": "emlwcmVjcnVpdGVy-3215",
    "title": "Longhand",
    "job_type": "full_time",
    "latitude": "40.71427000",
    "longitude": "-74.00597000",
    "city": "New York",
    "state": "NY",
    "country": "US",
    "employer_id": "emlwcGFydG5lcg-042",
    "employer_name": "Stenographica, Ltd",
    "description": "<div>We need someone to write things by hand, on paper, legibly, in cursive.</div>"
  },
  "success": true
}

The GET operation will also include the additional field job_status (in addition to the job) with one of the values listed below:

Job API Change Log

Questions API

This section provides API specification for the optional Questions to be attached to an existing job listing, sometimes known as screening or pre-screening questions — a short series of questions presented to your applicants during the application process.

Questions API Operations

The endpoint, https://api.ziprecruiter.com/partner/v0/job/<JOB_ID>/questions can be used for basic CRUD operations.

URL Parameter

JOB_ID - The ID of the job to which the Questions are attached.

Post Questions

The body of the request must contain a json representation of the questions, described below. The questions for the job (identified by JOB_ID) cannot already exist.

curl https://api.ziprecruiter.com/partner/v0/job/emlwcmVjcnVpdGVy-3215/questions \
  -H "Authorization: Basic meowmeowmeow" \
  -X POST \
  -H "Content-Type: application/json" \
  -d @questions.json

HTTP Request

POST https://api.ziprecruiter.com/partner/v0/job/<JOB_ID>/questions

Parameters

The questions are json-encoded object in the content. The question model is described below.

Update Questions

The body of the request must contain a json representation of the questions, described below. The questions for the job (identified by JOB_ID) may already exist, which will be overwritten.

curl https://api.ziprecruiter.com/partner/v0/job/emlwcmVjcnVpdGVy-3215/questions \
  -H "Authorization: Basic meowmeowmeow" \
  -X PUT \
  -H "Content-Type: application/json" \
  -d @questions.json

HTTP Request

PUT https://api.ziprecruiter.com/partner/v0/job/<JOB_ID>/questions

Get Questions

This endpoint retrieves the questions attached to the job specified by JOB_ID.

curl "https://api.ziprecruiter.com/partner/v0/job/emlwcmVjcnVpdGVy-3215/questions" \
  -H "Authorization: Basic meowmeowmeow"

HTTP Request

GET https://api.ziprecruiter.com/partner/v0/job/<JOB_ID>/questions

Delete Questions

This request deletes all the questions attached the job specified by JOB_ID.

curl "https://api.ziprecruiter.com/partner/v0/job/emlwcmVjcnVpdGVy-3215/questions" \
  -H "Authorization: Basic meowmeowmeow" \
  -X DELETE

HTTP Request

DELETE https://api.ziprecruiter.com/partner/v0/job/<JOB_ID>/questions

The Question Model

A sample list of questions, in json:

[
  {
    "question_id": "pet_name",
    "type"       : "text",
    "question"   : "What is your pet's name?",
    "required"   : true
  },
  {
    "question_id": "pet_age",
    "type"       : "text",
    "format"     : "integer",
    "question"   : "How old is your pet, in months?",
    "min"        : 1,
    "max"        : 250
  },
  {
    "question_id": "pet_weight",
    "type"       : "text",
    "format"     : "decimal",
    "question"   : "What is your pet's weight, in kg?",
    "min"        : 0.01,
    "max"        : 250
  },
  {
    "question_id": "pet_adopt_date",
    "type"       : "date",
    "question"   : "When was your pet adopted, or born into your care?",
    "min"        : "1970-10-20",
    "format"     : "YYYY-MM-DD"
  },
  {
    "question_id": "pet_sleep_pref",
    "type"       : "select",
    "question"   : "Where does your pet like to sleep?",
    "options"    : [
      {"value": "bed",   "label": "My Bed"},
      {"value": "floor", "label": "The Floor"},
      {"value": "alone", "label": "Its Bed"},
      {"value": "na",    "label": "None of the Above"}
    ]
  },
  {
    "question_id": "pet_nose_color",
    "type"       : "multiselect",
    "question"   : "What colors are present on your pet's nose?",
    "options"    : [
      {"value": "0", "label": "Pink"},
      {"value": "1", "label": "Brown"},
      {"value": "2", "label": "Liver"},
      {"value": "3", "label": "Yellow"},
      {"value": "4", "label": "Green"},
      {"value": "5", "label": "None of the Above"}
    ]
  },
  {
    "question_id": "pet_photo",
    "type"       : "upload",
    "question"   : "Please submit your cutest photo of your pet.",
    "required"   : true
  }
]

The questions are expressed as an array of objects, each object representing a question, encoded as json in the request or response body.

The following fields are available in all questions, regardless of type. All are required except for the required field. All fields should be text values unless specified otherwise.

Supported Question Types

Questions API Response

The response to a successful request is a json object, as the example shown at right.

{
  "success"  : true,
  "job_id"   : "emlwcmVjcnVpdGVy-3215",
  "questions": [
    {
      "question_id": "pet_name",
      "type"       : "text",
      "question"   : "What is your pet's name?",
      "required"   : true
    },
    {
      "question_id": "pet_age",
      "type"       : "text",
      "format"     : "integer",
      "question"   : "How old is your pet, in months?",
      "min"        : 1,
      "max"        : 250,
      "required"   : false
    },
    {
      "question_id": "pet_weight",
      "type"       : "text",
      "format"     : "decimal",
      "question"   : "What is your pet's weight, in kg?",
      "min"        : 0.01,
      "max"        : 250,
      "required"   : false
    },
    {
      "question_id": "pet_adopt_date",
      "type"       : "date",
      "question"   : "When was your pet adopted, or born into your care?",
      "format"     : "YYYY-MM-DD",
      "min"        : "1970-10-20",
      "required"   : false
    },
    {
      "question_id": "pet_sleep_pref",
      "type"       : "select",
      "question"   : "Where does your pet like to sleep?",
      "options": [
        { "value": "bed",   "label": "My Bed" },
        { "value": "floor", "label": "The Floor" },
        { "value": "alone", "label": "Its Bed" },
        { "value": "na",    "label": "None of the Above" }
      ],
      "required" : false
    },
    {
      "question_id": "pet_nose_color",
      "type"       : "multiselect",
      "question"   : "What colors are present on your pet's nose?",
      "options": [
        { "value": "0", "label": "Pink" },
        { "value": "1", "label": "Brown" },
        { "value": "2", "label": "Liver" },
        { "value": "3", "label": "Yellow" },
        { "value": "4", "label": "Green" },
        { "value": "5", "label": "None of the Above" }
      ],
      "required" : false
    },
    {
      "question_id": "pet_photo",
      "type"       : "upload",
      "question"   : "Please submit your cutest photo of your pet.",
      "required"   : true
    }
  ]
}

Features API

This section provides API specification for adding optional Features to an existing job. Features are paid add-ons that enhance the distribution of a job.

Available Features

TrafficBoost

TrafficBoost provides increased placement for your job on ZipRecruiter.com, partner sites, and in job alert emails. Your job gets an increased ranking score, which boosts its visibility in search results. Apply a TrafficBoost, and your job is promoted for up to 30 days or until it receives the specified number of views.

TrafficBoost has three levels that specify the number of views a boosted job has before the boost expires:

Features API Operations

The endpoint, https://api.ziprecruiter.com/partner/v0/job/JOB-ID/features can be used for basic CRUD operations.

URL Parameter

JOB_ID - The ID of the job to which the Features are attached.

POST/UPDATE Features

Example call to add a Feature to the jobs. JOB_ID in the URL must be replaced by the Job_ID of the job that will have the feature applied.

curl https://api.ziprecruiter.com/partner/v0/job/JOB_ID/features \
  -H "Authorization: Basic meowmeowmeow" \
  -X POST \
  -H "Content-Type: application/json" \
  -d @features.json

Example json file to add a single trafficboost. "single" can be replaced with "double" or "triple" depending on desired level.

{
    "features": {
        "trafficboost": "single"
    }
}

A POST/UPDATE request must be used to add a feature to an existing job. Once a TrafficBoost has been applied to a job, it cannot be removed or changed to another level.

The request must contain a valid payload as listed in the example and Features model.

GET Features

Example call to get the current status of Features on a job. JOB_ID in the URL must be replaced by the Job_ID of the job that the user wants to find the status of.

curl https://api.ziprecruiter.com/partner/v0/job/JOB_ID/features \
  -H "Authorization: Basic meowmeowmeow" \
  -X GET

A GET request can be used to find the current state of Features associated with the Job_ID in the request URL. This will list all possible features that can be associated with a job along with the status, with it being set to none if it is not set.

Features API Responses

A successful request will display success and the current TrafficBoost status.

{
    "success": true,
    "features": {
        "trafficboost": "single"
    }
}

A request may also go through, but the user may lack a valid payment method or other issues may occur in which the status will not change.

{
    "success": true,
    "features": {
        "trafficboost": "none"
    }
}

Example responses are shown to the right. A request will always show the status of the Features e.g. if a user has a valid job and permissions but the call fails, the TrafficBoost status will unchanged from what it previously was. If a job already has TrafficBoost applied and a request is made, it will display the current boost as changes cannot be made once they are applied.

The Features Model

An example json body in a request to add a double TrafficBoost:

{
    "features": {
        "trafficboost": "double"
    }
}

The Features model is represented as a JSON object in the request body, as shown in the examples.

The required fields are features and trafficboost.

Errors

The Partner API uses the following error codes:

Error Code Meaning
400 Bad Request -- Your request is invalid.
401 Unauthorized -- Your API key is wrong.
500 Internal Server Error -- We had a problem with our server. Try again later.
503 Service Unavailable -- We're temporarily offline for maintenance. Please try again later.

Most any error you get from the API is a 400, you'll get back a body like the one shown at right.

{
  "error": "Invalid Payload: Property is required: /job_id.",
  "success": false
}

The error property will describe the nature of the problem, and can include, for example:

Apply Webhook

ZipRecruiter aims to make the job posting process simple from end to end. This includes automated job posting from your website or Applicant Tracking System (ATS) platform, as well as delivering new job applications back to it.

Requirements

ZipRecruiter Apply Webhook requires the following:

  1. You must set up a Job API integration (optionally XML Feed Import) for automatic job posting, in at least the minimal format. This allows us to post and unpost jobs in line with your ATS, as well as provides us with your ATS’s job identifiers so applicants can be delivered back to a specific job in your ATS. This feed does not need url tags; they will be ignored.

  2. You must provide an HTTPS endpoint which accepts a POST request in JSON format. This is a single URL which handles submissions for all of your jobs. You will need to let us know which content type you need when you furnish the URL for this endpoint.

Transport

The ZipRecruiter Apply Webhook makes an HTTPS POST to the endpoint of your choosing. We do not deliver to non-HTTPS services.

This endpoint must furnish a verifiable SSL certificate signed by a well-known Certificate Authority (such as those trusted by Mozilla). If your web browser can visit the endpoint without generating a security warning, we can support it.

Format

Example JSON Request

    POST /job/apply HTTP/1.1
    Host: yourhost.example.com
    Content-Type: application/json

    {
      "response_id": "a39bd9a",
      "job_id": "1000002”,
      "name": "Tom Foolery",
      "first_name": "Tom",
      "last_name": "Foolery",
      "email": "tf@example.org",
      "phone": "+1 5555551942",
      "resume": "JVBERi0xLjUKJb/3ov4KMiAwIG9iago8PCAvTGluZWFyaXplZCAxIC9MIDE3ODA3IC9IIFsgNjg3IDEyNiBdIC9PIDYgL0UgMTc1MzIgL04gMSAvVCA...jIxNgolJUVPRgo=",
      "attributes": {
        "tracking_code": "r2d2c3po"
      },
      "profile": {
        "executive_summary": "Business Analyst with over 5 years of experience supporting business solution software and analyzing business operations.",
        "mobile": "+1 555 555-1942",
        "job_records": [
            {
              "start_date_precision": "YearMonthDay",
              "position": "Business Analyst",
              "description": "Analyzing data",
              "start_date": "2012-09-01",
              "end_date_precision": null,
              "employer": "Toms",
              "current": "1",
              "end_date": null
            },
            {
              "position": "Business Analyst",
              "start_date_precision": "YearMonthDay",
              "start_date": "2010-09-01",
              "end_date_precision": "YearMonthDay",
              "description": "Still Analyzing data",
              "current": "0",
              "employer": "Fooleries",
              "end_date": "2012-05-01"
            }
        ],
      "text_resume": "BUSINESS ANALYST SAMPLE RESUME",
      "city": "Santa Monica",
      "state": "CA",
      "postal_code": "90401"
    }
  }

The Content-Type request header will be set to application/json.

Response

Example JSON Response

  {
    "candidate_id": "44016-ksrm",
    "job_id": "V-8632",

    "additional_data": {
      "summary": {
        "application": {
          "status": "success",
          "error": null
        },
        ...
      }
    }
  }

A 200 OK or 2xx HTTP response is considered a successful delivery. Other response codes are considered unsuccessful, and are logged and handled, and may be retried automatically or manually.

For successful applications, the response body can include either:

This information will be stored on our side to support future API calls with these identifiers (such as the Hiring Signal API).

Any additional data can be included as part of the additional_data field.

The basics are

Fields

All field values are alphanumeric strings unless specified otherwise.

Signatures

You may optionally enable signatures for network requests from Apply Webhook.

After signatures are enabled, you must:

  1. Verify the signature.

  2. Check that the current time is within a few seconds of the timestamp in the request.

Benefits

Secret Exchange

Upon request, we will provide a secret string via a one-time access link to you, leveraging a secure delivery method. This secret will be shared within the same ZipRecruiter org. You should keep this secret safe from potentially malicious actors.

Signature

Full Signature Headers Example in HTTP Request

POST http://localhost/apply
Accept-Encoding: gzip
Authorization: Basic 123abc
User-Agent: Test-WWW-Mechanize/1.50
Content-Length: 22978
Content-Type: application/json
X-ZipRecruiter-Signature: c//+/Qo4wuJyzFENikeVOabKFXCJ5wLHzlTqkxHgZTk=
X-ZipRecruiter-Signature-Timestamp: 2021-02-12T15:49:55.214Z
X-ZipRecruiter-Signature-Version: v1; sha256

{"response_id": "1", "first_name": "John", "last_name": "Doe"}

Implementation Sample

import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Mac;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
public class Main {
    public static String encode(String key, String data) {
        Mac sha256_HMAC = null;
        try {
            sha256_HMAC = Mac.getInstance("HmacSHA256");
        }
        catch(NoSuchAlgorithmException e) {
            System.out.println("Something is wrong");
        }
        SecretKeySpec key_spec = new SecretKeySpec(key.getBytes(StandardCharsets.US_ASCII), "HmacSHA256");
        try {
            sha256_HMAC.init(key_spec);
        }
        catch(InvalidKeyException e) {
            System.out.println("Something is wrong");
        }
        return Base64.getEncoder().encodeToString(sha256_HMAC.doFinal(data.getBytes(StandardCharsets.US_ASCII)));
    }
    public static void main(String[] args) {
        String message = getMessage();
        String secret = "my secret";
        String signature = encode(secret,message);
        String sent_signature = "c//+/Qo4wuJyzFENikeVOabKFXCJ5wLHzlTqkxHgZTk=";
        System.out.println(sent_signature.equals(signature));
    }
}
import hmac
import hashlib
import base64
message = getMessage()
secret = 'my secret'
signature = base64.b64encode(hmac.new(bytes(secret , 'ascii'), msg = bytes(message , 'ascii'), digestmod = hashlib.sha256).digest()).decode()
sent_signature = 'c//+/Qo4wuJyzFENikeVOabKFXCJ5wLHzlTqkxHgZTk=';
print(signature == sent_signature)
import (
    b64 "encoding/base64"
    "fmt"
    "crypto/sha256"
    "crypto/hmac"
)
func main() {
    message := getMessage()
    secret := "my secret"
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(message))
    signature := b64.StdEncoding.EncodeToString(mac.Sum(nil))
    sent_signature := "c//+/Qo4wuJyzFENikeVOabKFXCJ5wLHzlTqkxHgZTk="
    fmt.Println(signature == sent_signature)
}

Once signatures are enabled and secrets have been exchanged, we will send 3 signature-related headers with every Apply Webhook delivery to your endpoint: "X-ZipRecruiter-Signature", "X-ZipRecruiter-Signature-Version", and "X-ZipRecruiter-Signature-Timestamp".

X-ZipRecruiter-Signature: <signature>

<signature>: String of characters returned by signature algorithm.

Example: c//+/Qo4wuJyzFENikeVOabKFXCJ5wLHzlTqkxHgZTk=

You can verify this signature as follows:

  1. Take the API Webhook payload. For example:

- {"response_id" : "1","first_name" : "John", "last_name" : "Doe"}

  1. Take the timestamp from X-ZipRecruiter-Signature-Timestamp:

- 2021-02-12T15:49:55.214Z"

  1. Concatenate timestamp and payload, separated by a dot (.). Note that the payload needs to be from the request body without altering it.

- 2021-02-12T15:49:55.214Z.{"response_id" : "1","first_name" : "John", "last_name" : "Doe"}

  1. Create a base64-encoded HMAC SHA256 hash out of timestamp.payload with your secret without breaking the encoded string into lines.

- c//+/Qo4wuJyzFENikeVOabKFXCJ5wLHzlTqkxHgZTk=

  1. Verify that the calculated signature is identical to the signature in X-ZipRecruiter-Signature with a case-sensitive equality check.

X-ZipRecruiter-Signature-Version: <version; comment>

<version; comment>: a version followed by an ignorable comment, separated by a semi-colon (;). The comment indicates the hashing algorithm. Versioning indicates our method for computing signatures.

Example: v1; sha256

X-ZipRecruiter-Signature-Timestamp: <timestamp>

import java.util.Calendar;
import java.time.Instant;
import java.time.Duration;
public class Main {
    public static void main(String[] args) {
        Instant sent_time = get_time();
        Instant current_time = Calendar.getInstance().toInstant();
        Duration duration = Duration.ofSeconds(5);
        Instant max_window = sent_time.plus( duration );
        System.out.println(current_time.isBefore(max_window));
    }
}
from datetime import datetime, timedelta
sent_time = get_time()
current_time = datetime.now()
max_window = sent_time + timedelta(0,5)
print(current_time < max_window)
import (
    "fmt"
    "time"
)
func main() {
    sent_time := get_time()
    current_time :=  time.Now()
    max_window := sent_time.Add(time.Second * time.Duration(5))
    fmt.Println(current_time.Before(max_window))
}

<timestamp>: a date-timestamp in UTC conforming to RFC 3339.

Example: 2021-02-12T15:49:55.214Z

To prevent replay attacks, you should check that the timestamp in the header is within a few seconds of the current timestamp. Too large of a window size will result in false negatives (i.e., not catching a replay attack if one has happened). Too small of a window size will result in false positives (i.e., rejecting valid requests).

Caveat(s):

Frequently Asked Questions

Do you provide a sandbox for testing?

Unfortunately, at the moment we do not have a sandbox for testing the integration. However, we can make arrangements to provide a test import and a few test applications if needed.

Do I need a URL for every job?

No. Please provide only one POST URL for the webhook, and the software receiving that POST can associate applications with jobs by the provided job_id field.

Can I add an interview or screening questions to my jobs?

This is an advanced feature. We support a standard interview format and a newly updated question format, as well as delivery of applicant answers through this webhook. If you are interested, please contact us. If your ATS platform supports interviews but doesn't support our format, please reach out. We may be able to help on a case-by-case basis. Also, please reach out if you intend to use the new question format.

Hiring Signals API

Introduction

This API permits ZipRecruiter partners to report hiring-related events regarding applications we have forwarded to them.

Version documented: v0

There is a single endpoint event that accepts POST requests to report hiring events.

The body of the request must contain a json representation of the event, described below.

Authentication

To authorize, use this code:

# With shell, you can just pass the correct header with each request
curl https://api.ziprecruiter.com/hiring-signal/v0/event \
  -H "Authorization: Basic meowmeowmeow" ...

Make sure to replace meowmeowmeow with the properly encoded API key.

Partner API uses Basic authentication to allow access to all endpoints. You will be provided an API key; please keep it secret.

Operations

Post an event

The hiring event is an json-encoded object in the content.
The event model is described below.

POST https://api.ziprecruiter.com/hiring-signal/v0/event

curl https://api.ziprecruiter.com/hiring-signal/v0/event \
  -H "Authorization: Basic meowmeowmeow" \
  -X POST \
  -H "Content-Type: application/json" \
  -d @event.json

The Event Model

The hiring event is represented as a JSON object in the request body, as shown in the examples.

Fields are optional unless otherwise specified.

A minimum event, in json:

{
   "zr_application_id": "681d065d",
   "event": "contacted",
   "event_timestamp": "2021-01-25T18:46:04Z"
}

A sample rejected event, with additional fields:

{
   "zr_application_id": "681d065d",
   "event": "rejected",
   "status_name": "Knock-Out",
   "status_group": "Rejection",
   "reason": "automatic",
   "event_timestamp": "2021-01-25T18:46:04Z",
   "additional_data": {
     "total_stages": 8,
     "external_id": "681d065d",
     "current_stage": 1,
     "last_activity_at": "2022-04-13T07:59:37.048-07:00",
     "disposition": "Knock-Out",
     "milestone": "Application",
     "applied_at": "2022-04-10T16:24:01.366-07:00"
     ...
   },
   "job_id": "V-8632",
   "candidate_id": "44016-ksrm"
}

A sample hired event, with additional fields:

{
   "zr_application_id": "681d065d",
   "event": "hired",
   "status_name": "Hired",
   "event_timestamp": "2021-01-25T18:46:04Z",
   "additional_data": {
     "status": "Hired",
     "sub_status": "Hired for Different Position",
     "current_stage": 5,
     "total_stages": 5,
     "last_activity": "2021-01-25T18:49:14Z",
     ...
   },
   "job_id": "V-8632",
   "candidate_id": "44016-ksrm"
}

One of the below listed identifiers is required. You must provide either:

  1. zr_application_id, or
  2. application_id, or
  3. job_id and candidate_id

The basics are

In addition, following fields are required.

One of the below listed descriptive fields is required. When possible, provide all of the following: - status_name - customer- or partner-defined name/label for the application's current status, unaltered. (It may be a reason a candidate declined or was rejected, or a step in the candidate hiring funnel) - status_group - customer- or partner-defined group name/label for the application's current status, unaltered, if any. If the status values (status_name) are grouped into categories, then this field is the category. It may be a stage in the hiring funnel, if stages are further characterized by dispositions - event - enum, can be one of the following values:

event meaning
received Default stage when an application is received and viewable in the ATS.
viewed An application was viewed and/or reviewed by an employer but no action has been taken.
contacted Candidate has either been contacted by phone, email, etc for an initial screening, or there is an intent to contact the candidate for screening.
assessment Optional stage for more technical/skills jobs when a candidate will be given or has received a quiz, skills assessment, case study, test etc.
interviewed Candidate has been scheduled or has completed an in-person, video, or phone interview. (Made it past the initial recruiter/HR screening).
offered An offer of employment has been sent to a candidate.
prehire The candidate is undergoing or will next require a screening check, such as background check, medical checks, reference checks, immigration validation, etc.
hired The candidate has accepted an offer of employment.
rejected The application has been rejected, knocked out of the system, or reassigned.
unable_to_map Use this status only when you cannot find a corresponding status. When using this status, must provide status_name and/or status_group.

Additional very helpful fields: - additional_data - a free-form object containing the partner's description of the application's current status. This might include stage names, numbers, timestamps, etc. - reason - enum. given with the rejected event to categorize the rejection. one of:

reason meaning
automatic Automatically rejected due to being knocked out by the system or not meeting the required criteria.
closed The role was filled by another candidate, or closed.
failed_checks The candidate failed background, pre-employment, etc checks.
uncertified The candidate lacks a required license or certification.
unresponsive The candidate was uncontactable, or did not respond.
out_of_area Applicant is not in required area, or will not relocate.
unavailable Applicant turned down the offer, or is not available in required time frame.
unqualified Applicant was explicitly rejected for unspecified reason.
consider_for_other_role Applicant was recommended for another role within the company.
hired_for_other_role Applicant was rejected because they were already hired for another role within the company.
other Any reason not covered by above.

Event Response

The status 200 response to a successful request is a json object (encoded into the body), as the example shown at right.

{
  "zr_application_id": "681d065d",
  "success": true
}

Event Errors

If the given data does not pass validation, the response will have HTTP status 400, and returned json object will have an errors attribute: an array of one or more objects describing validation errors.

{
  "status": 400,
  "errors": [{
    "path": "\/body\/event",
    "message": "Missing property."
  }]
}

Sample errors:

A status 404 response indicates the given zr_application_id does not exist, and the body makes that clear.

{
  "status": 404,
  "errors": [{
    "path": "\/body\/zr_application_id",
    "value": "56fe30a1",
    "message": "Application does not exist"
  }]
}

Change Log

Feed Import

Requirements

Note

Please provide the proper character encoding for your feed in the XML header. The Example Feed illustrates UTF-8, however this may or may not be the case for you. Please check with your system administrator if your need clarification. If you’re not specifying the proper encoding, your listings may appear with unusual characters.

A feed must consist of a <source> tag containing one or more <job> tags. All job content must be inside CDATA sections or properly encoded with XML entities to avoid issues processing your XML feed. All XML tags are case-insensitive unless otherwise specified.

Feeds must provide all currently open jobs, not jobs added since the last feed request. Jobs not included in your feed will be removed from your account, our search results, and our network.

The referencenumber provided for each job must be unique to that job. The referencenumbers of jobs found in your feed are compared to those on your ZipRecruiter account in order to determine what jobs should be added, removed, or updated.

Minimum Requirements for Jobs to be Delivered

You may include additional optional fields such as jobtype, experience, or interview_json. If included, these fields must contain only the explicit values noted in this document, otherwise the job will be rejected.

Example Feed

<?xml version="1.0" encoding="UTF-8"?>
<source>
  <!-- Optional Metadata Fields -->
  <lastBuildDate>Wed, 27 Aug 2014 01:49:49 GMT</lastBuildDate>
  <publisherurl>http://dev.ziprecruiter.com:4014</publisherurl>
  <publisher>ZipRecruiter</publisher>
  <!-- End Optional Metadata Fields -->
  <job>
    <referencenumber>a-unique-job-id-123</referencenumber>
    <requisition_id>a-job-requisition-id-123</requisition_id>
    <title>Auto Tech.</title>
    <description><![CDATA[Hiring a skilled Auto Technician <b>ASAP</b>!]]></description>
    <country>US</country>
    <city>Santa Monica</city>
    <state>CA</state>
    <postalcode>90755</postalcode>
    <company>Euro Car Care, Inc</company>
    <date>2014-07-31T07:27:14</date>
    <!-- Candidate Delivery Fields --> 
    <url><![CDATA[http://jobs.dev.ziprecruiter.com:4014/job/Auto-Tech/cc68d2f943/?source=ziprecruiter-feed]]></url>
    <!-- Additional Optional Fields --> 
    <address>1615 Ocean Ave</address>
    <jobtype>Full-Time</jobtype>
    <accept_remote>0<accept_remote>
    <experience>mid</experience>
    <education>ged</education>
    <compensation_interval>hourly</compensation_interval>
    <compensation_min>24.00</compensation_min>
    <compensation_max>48.00</compensation_max>
    <compensation_currency>USD</compensation_currency>
    <benefits>
     <medical>1</medical>
     <dental>1</dental>
     <vision>0</vision>
     <life_insurance>1</life_insurance>
     <retirement_savings>0</retirement_savings>
    </benefits>
  </job>
  <!-- Further <job>s follow... -->
</source>

Optional Metadata Fields: These fields may be included for feed-compatibility with other systems.

Candidate Delivery Fields: Normally only one of these Candidate Delivery Fields should be included, but both are shown in this example.

Additional Optional Fields: See Additional Optional Fields - Get More Exposure

Job Fields

Each <job> tag represents one job listing. The following are the core fields for jobs. Each should be a single tag per job, containing nothing but the content for that field. See the Example Feed for reference.

Additional Optional Fields - Get More Exposure

We accept many more job fields that increase your search visibility as well as provide more detail to candidates in terms of compensation and requirements.

See Extended Job Fields for information on additional job fields we can support. Adding job address and compensation details is highly recommended for maximum visibility. These extended fields include:

Feed Updates & Delivery

We require a full feed file, so that at any point in time you are specifying exactly which jobs you would like to have included on your account. A feed URL that only returns jobs which are new or changed since our last check will cause any prior jobs to be unposted from your account.

We’ll read and update your feed 4 times per day, approximately every 6 hours. It takes up to a few hours for updates to propagate fully and 24-48 hours for the updates to reach our job board partners. If your feed is preprocessed by a third party, this may introduce additional delays.

We currently accept files delivered via HTTPS or HTTP, and can support "Basic" authentication.

Feed Import Frequently Asked Questions

How do I close jobs when using the XML feed?

Remove the jobs you want to close from your XML feed, and they’ll be closed the next time the feed is updated.

Can I make changes to jobs using the ZipRecruiter web site?

Yes, you can update jobs posted via XML on the ZipRecruiter web site, but be aware that the next time your feed is updated via XML, your job will be updated to match what’s in the XML, which could overwrite any changes you made on the web site.

Can I "refresh" my old jobs by changing the dates?

No. If the values in <date> are changed on a job that was previously imported, the Jobs folder will show the date when the job was originally imported. However, we refresh the "posted on" date shown to jobseekers every 30 days.

Are duplicate entries allowed?

No, duplicate listings with the same job_id will cause the import to fail completely.

Extended Job Fields

All of the fields detailed in this section are optional, but can improve the jobseeker experience and candidate quality for your listings. Everything listed here must be included inside the <job> tag for each job to which it applies. Examples can be found in the Example Feed.

Warning

If you use the exact same feed on other platforms, they may have trouble with these extended tags and require a copy of your feed without them.

Location

Employment and Compensation

Benefits

<benefits>
  <vision>1</vision>
  <medical>1</medical>
  <dental>1</dental>
  <life_insurance>1</life_insurance>
  <retirement_savings>1</retirement_savings>
</benefits>

Simple benefits info can be added to your job listing with a <benefits> tag. This tag may contain any of the following tags, which, if present with any content other than 0, will indicate the corresponding benefit is offered with the position. Acceptable benefits tags are vision, medical, dental, life_insurance, and retirement_savings. Any tags that are absent will not be shown as benefits on your job listing.

Applicant Requirements

Applicant Interview

Each job may have a series of interview questions for the candidate, and this interview can be specified in your job feed. The optional <interview_json> tag adds an interview to the job. This tag should contain JSON content, and wrapping that JSON in a CDATA section is recommended. See The Question Model for details on the expected JSON content.

If you have an ATS integration or other partnership, candidates' answers through interviews added by feeds or through your ZipRecruiter account may also be sent to your ATS platform.

Example Single-Question Interview, inside an XML snippet..

<interview_json>
  <![CDATA[
   [
     {
       "id": "color",
       "type": "select",
       "question": "What is your favorite color?",
       "options": [
         {"value": "blue",  "label": "Blue"},
         {"value": "red",   "label": "Red"},
         {"value": "green", "label": "Green"}
       ]
     }
   ]
  ]]>
</interview_json>

Example Multi-Question Interview with grouping and conditionality, inside an XML snippet.

<interview_json>
  <![CDATA[
    [
      {
        "id": "ParentQID",
        "type": "select",
        "question": "Do you want to answer next question?",
        "group_number":1,
        "options": [
          { "value": "Yes", "label": "Yes" },
          { "value": "No",  "label": "No" }
        ]
      },
      {
        "id": "ChildQID",
        "type": "select",
        "question": "Answer this question?",
        "group_number":1,
        "condition" : {
          "id" : "ParentQID",
          "value": "Yes"
        },
        "options": [
          { "value": "1", "label": "one" },
          { "value": "2", "label": "two" }
        ]
      }
    ]
  ]]>
</interview_json> 

If the <interview_json> tag is empty or missing, no changes to existing questions/answers will be made.

To remove all questions from an existing job, send an empty json array as the content of the tag, e.g. <interview_json> <![CDATA[ [] ]]> </interview_json>.

Partner Attributes

Each job may include Partner Attributes, arbitrary key/value data that may be used for ATS integrations or other custom behaviors. Within <partner_attributes>, tags of any name are allowed, and any value is accepted, but tags may not be nested.

Example Partner Attributes.

<partner_attributes>
  <tracking_code>r2d2c3po</tracking_code>
</partner_attributes>

References

JSON Schema

This following specifications are expressed as JSON Schema documents, which are used for input validation at ZipRecruiter, and can be used for output and testing validation. The current version of this schema can always be found at the URLs below.

The following tools can be used from a web browser to test a JSON document against a JSON Schema.

Be aware that JSON Schema is currently in an ongoing draft process, and tools may not support the most recent draft specification. Most schemas can be translated back to older drafts in order to support outdated tools.

JSON Schemas

Version V1

Version V2

Changelog

2023-06-14

2023-04-27

2022-01-20

2021-08-12

2021-04-14

2020-12-14