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 beurn: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:
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: |
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:
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.