Sync Applications

All job applications should be sent via the Middleware Platform whenever they are created or updated in your ATS. Use your job application identifier when syncing via the API.

Throttle Limits

Throttle Limits Requests Per Day (UTC) Records Per Minute
Application maximum 100,000 10,000

Note

Please submit batch calls grouped with no more than 100 records in sequential order.

Sync Applications

Use the following endpoint to create and update records:

PUT https://api.linkedin.com/v2/atsApplications?ids[0].atsJobApplicationId={id1}&ids[0].dataProvider=ATS&ids[0].integrationContext={organization URN}&ids[1].atsJobApplicationId={id2}&ids[1].dataProvider=ATS&ids[1].integrationContext={organization URN}

For each id specified in the request:

  • Use your job application id as the value of id[i].atsJobApplicationId
  • Use the customer's organization id as the value of id[i].integrationContext. The format should be urn:li:organization:{id}

Note

When updating an existing record, you must specify all available fields. Omitting a field will set it to null.

Note

The combination of the above 3 mandatory query parameters will be used as a unique identifier for your synced job application within LinkedIn in later requests.

API Request Body Fields

Field Description Format Required
atsCandidateId Candidate's unique identifier in your ATS String No
atsCreatedAt Date record was created in your ATS. Must be greater than 0. UTC Epoch Milliseconds Yes
atsLastModifiedAt Date record was last updated in your ATS. Must be greater than 0. Must be greater than or equal to atsCreatedAt. UTC Epoch Milliseconds Yes
atsJobPostingId Unique identifier in your ATS of the job being applied to. Minimum length: 1. String Yes
atsJobPostingName Name in your ATS of the job being applied to. Minimum length: 1. String Yes
candidateEmail Candidate email address as entered on the application. Email must follow X@X.X pattern.
*Candidate email is required whenever available, to match ATS candidates with LinkedIn members. If candidate email is not available for an application, member matching will be attempted using the candidate’s name, job title, and company.
EmailAddress Yes if available
dispositionReason Indicates the reason the candidate was not hired. Should otherwise be set to null String No
firstName Candidate's first name as entered on the application. Minimum length: 1. String Yes
lastName Candidate's last name as entered on the application. Minimum length: 1. String Yes
source Where the application originated within your ATS. For example, "Job Board" or "Referral". Minimum length: 1. String Yes

Sample Request (query tunneled)

Note

In production, requests must use query tunneling to avoid reaching maximum URL length. You can use non-tunneled requests when request URLs are shorter than length limits, for testing purposes, and for troubleshooting. Please check details on Query Tunneling Migration Guide.

curl -L -X POST 'https://api.linkedin.com/v2/atsApplications' \
-H 'X-HTTP-Method-Override: PUT' \
-H 'Content-Type: multipart/mixed; boundary=xyz' \
-H 'x-restli-method: batch_update' \
-H 'Authorization: Bearer {token}' \
--data $'--xyz\r\n
Content-Type: application/x-www-form-urlencoded\r\n\r\nids[0].atsJobApplicationId=APPL123&ids[0].dataProvider=ATS&ids[0].integrationContext=urn:li:organization:2414183&ids[1].atsJobApplicationId=APPL456&ids[1].dataProvider=ATS&ids[1].integrationContext=urn:li:organization:2414183\r\n--xyz\r\n 
Content-Type: application/json\r\n\r\n
{
  "entities": {
    "atsJobApplicationId=APPL123&dataProvider=ATS&integrationContext=urn:li:organization:2414183": {
      "atsCandidateId": "CAND123",
      "atsCreatedAt": 1484864187000,
      "atsLastModifiedAt": 1484864187000,
      "atsJobPostingId": "JOBP123",
      "atsJobPostingName": "Senior Tester",
      "candidateEmail": "bar@example.com",
      "firstName": "Peter",
      "lastName": "Griffin",
      "source": "REFERRAL"
    },
    "atsJobApplicationId=APPL456&dataProvider=ATS&integrationContext=urn:li:organization:2414183": {
      "atsCandidateId": "CAND456",
      "atsCreatedAt": 1484864187000,
      "atsLastModifiedAt": 1484864187000,
      "atsJobPostingId": "JOBP456",
      "atsJobPostingName": "Senior Manager",
      "candidateEmail": "foo@example.com",
      "firstName": "Maude",
      "lastName": "Flanders",
      "source": "Job Board"
    }
  }
}\r\n--xyz--'

Sample Request(without query tunneling)

curl -L -g -X PUT 'https://api.linkedin.com/v2/atsApplications?ids[0].atsJobApplicationId=APPL123&ids[0].dataProvider=ATS&ids[0].integrationContext=urn%3Ali%3Aorganization%3A2414183&ids[1].atsJobApplicationId=APPL456&ids[1].dataProvider=ATS&ids[1].integrationContext=urn%3Ali%3Aorganization%3A2414183' \
-H 'Content-Type: application/json' \
-H 'x-restli-method: batch_update' \
-H 'Authorization: Bearer {token}' \
--data '{
  "entities": {
    "atsJobApplicationId=APPL123&dataProvider=ATS&integrationContext=urn:li:organization:2414183": {
      "atsCandidateId": "CAND123",
      "atsCreatedAt": 1484864187000,
      "atsLastModifiedAt": 1484864187000,
      "atsJobPostingId": "JOBP123",
      "atsJobPostingName": "Senior Tester",
      "candidateEmail": "bar@example.com",
      "firstName": "Peter",
      "lastName": "Griffin",
      "source": "REFERRAL"
    },
    "atsJobApplicationId=APPL456&dataProvider=ATS&integrationContext=urn:li:organization:2414183": {
      "atsCandidateId": "CAND456",
      "atsCreatedAt": 1484864187000,
      "atsLastModifiedAt": 1484864187000,
      "atsJobPostingId": "JOBP456",
      "atsJobPostingName": "Senior Manager",
      "candidateEmail": "foo@example.com",
      "firstName": "Maude",
      "lastName": "Flanders",
      "source": "Job Board"
    }
  }
}'

Sample Response

A successful request returns a 200 OK response code, and you will find the status of each entity in the response body.

Sample Response Body

{
    "errors": {},
    "results": {
        "atsJobApplicationId=APPL123&dataProvider=ATS&integrationContext=urn%3Ali%3Aorganization%3AaxQ23fJ": {
            "status": 204
        },
        "atsJobApplicationId=APPL456&dataProvider=ATS&integrationContext=urn%3Ali%3Aorganization%3AaxQ23fJ": {
            "status": 204
        }
    }
}

Note

Be sure to check the response for error statuses corresponding to individual entities you submit.

Delete Applications

Perform HTTP DELETE on /atsApplications endpoint when a customer deletes records.

API Details

More information is available here.

Resume Upload

To upload a resume, you first need to get a signed URL from LinkedIn and then use it to upload the file.

When uploading resume, LinkedIn recommends to abide by the following considerations:

  • Supported media formats are: PDF, DOC, or TXT
  • Ensure that the media file size does not exceed 10MB for all file types
  • Upload only one resume object per API call
  • Ensure that all calls to upload resume use OAuth 2.0 authentication

A typical workflow to upload your resume consists of the following steps:

  1. Create URL
  2. Upload Resume File

Create URL

Use the following endpoint to create URL:

POST https://api.linkedin.com/v2/hireMediaUrl 

API Authorization

This API is governed by OAuth 2.0 Authorization Framework and you must pass a bearer access token in your request as Authorization header. The access tokens are obtained via OAuth 2.0 Client Credentials flow. You must use your customer's application client id and client secret to obtain the access token.

Request Body Fields

Field Description Format Required
usageParameter Helps LinkedIn to uniquely identify the use case for which media upload is being done. For this use case, refer to the value as shown in the sample request below Complex Yes
contentType Type of the resume file. Valid values are:
  • "text/plain" for TXT file
  • "application/pdf" for PDF file
  • "application/msword" for DOC file
  • String Yes
    fileName Name of resume file String No
    blobTtl Duration for which the resume file is persistent in LinkedIn Timespan No
    hiringContext The contract which is signed by the customer with LinkedIn to use LinkedIn Recruiter services. Must be in the format urn:li:contract:{Id} Recruiter contract URN Yes

    Timespan

    Field DataType
    duration long
    unit ENUM values of the type: MILLISECOND, SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, YEAR

    Sample Request

    Headers:

    Authorization: Bearer {token}
    Accept:application/json
    Content-Type:application/json
    X-RestLi-Protocol-Version:2.0.0
    

    Request Body (resume will be persisted in Linkedin forever ):

    {
       "usageParameters":     
       {
          "recruiterSystemConnectResumeParameters" : {}
       },
       "contentType": "text/plain",
       "fileName": "resume.txt",
       "hiringContext": "urn:li:contract:123456"
    }
    

    Request Body (resume will be removed from Linkedin in 72 hours):

    {
       "usageParameters":     
       {
          "recruiterSystemConnectResumeParameters" : {}
       },
       "contentType": "text/plain",
       "fileName": "resume.txt",
       "blobTtl": {"duration" : 72, "unit" : "HOUR"},
       "hiringContext": "urn:li:contract:123456"
    }
    

    Sample Response

    A successful request returns a 201 HTTP response code. The response has an empty body and contains a signed URL in the x-linkedin-id response header.

    Sample Response Body

    HTTP/2 201 
    x-li-responseorigin: RGW
    location: /hireMediaUrl/https%3A%2F%2Fwww.linkedin.com%2Fambry%2F%3Fx-li-ambry-ep=IKNzqXRAxc1n6yCEaLQFyikjNuvufa9i6Ek2Or5V4RpUZu8AY9CRylnLjOWX6ABe05uiXOyr0rw6hvnNcDdJLEd1Kunfn...fdfr67h
    x-linkedin-id: https://www.linkedin.com/ambry/?x-li-ambry-ep=IKNzqXRAxc1n6yCEaLQFyikjNuvufa9i6Ek2Or5V4RpUZu8AY9CRylnLjOWX6ABe05uiXOyr0rw6hvnNcDdJLEd1Kunfn...fdfr67h
    x-restli-protocol-version: 2.0.0
    content-length: 0
    date: Thu, 17 Jun 2021 09:27:30 GMT
    

    Upload Resume File

    After getting a signed URL, you can now upload the resume file by calling the below API. This API expects header "Content-type" to be provided. Valid values are:

  • "text/plain" for TXT file
  • "application/pdf" for PDF file
  • "application/msword" for DOC file
  • Sample Request

    curl -i -H "Authorization: Bearer {token}" -H 'Content-Type: application/pdf' https://www.linkedin.com/ambry/?x-li-ambry-ep=AQEvzZSZZQH75QAAAXhws9LWCtrppkjlS2oJyQOrJA...1XFGyS1XfEvSo --data-binary @datafile
    

    Note

    To upload a plain text file, use header 'Content-Type:text/plain' instead of 'Content-Type: application/pdf' in the above sample.

    Sample Response

    A successful request returns a 201 HTTP response code. The response has an empty body and contains file location in the location response header.

    Sample Response Body

    HTTP/2 201 
    cache-control: no-cache, no-store
    pragma: no-cache
    expires: Thu, 01 Jan 1970 00:00:00 GMT
    location: /AAYUAgCvAAkAAQAAAAJFyLgwRkz_TmebRMrAixANdQ.txt
    x-li-uuid: HP2pABxWiRI1FsAisAAA==
    date: Thu, 17 Jun 2021 09:56:58 GMT
    content-length: 0
    

    Sync Application Resumes

    Use the following endpoint to create or update job application resume records:

    PUT https://api.linkedin.com/v2/atsApplications/atsJobApplicationId={id1}&dataProvider=ATS&integrationContext={organization URN}/resumes?ids={resume id 1}
    

    Request Body Fields

    Field Description Format Required
    atsCreatedAt Date record was created in your ATS UTC Epoch Milliseconds Yes
    resumeFile URN using format urn:li:ambryBlob:{location} AmbryBlobUrn Yes

    Sample Request

    Headers:

    Authorization: Bearer {token}
    x-restli-method: batch_update
    

    Request Body:

    {
      "entities": {
        "APPL_RESUME123": {
          "atsCreatedAt": 1484864187000,
          "resumeFile": "urn:li:ambryBlob:/AAYUAgCvAAkAAQAAAAJFyLgwRkz_TmebRMrAixANdQ.txt"
        }
      }
    }
    

    Sample Response

    A successful request will return a 200 OK response code, and you will find the status of each entity in the response body.

    Sample Response Body

    {
        "errors": {},
        "results": {
            "APPL_RESUME123": {
                "status": 204
            }
        }
    }
    

    Note

    If a job application has multiple resumes, only the newest copy will be used for candidate match. Resumes will be sorted based on "atsCreatedAt" of the ATS application resume in descending order.