DevSkiller API Overview
The DevSkiller REST API lets you interact with DevSkiller platform. You can use the API especially for:
- searching for an exam
- adding a candidate
- getting candidates list
- getting candidate details and results
API Endpoint
All API URLs are relative to https://api.devskiller.com/. For example, to get list of candidates you should call /candidates
endpoint at
https://api.devskiller.com/candidates
API Key
Each request must contain an API Key passed as HTTP header: X-Api-Key. To get your personal API key, please navigate to the Settings -> Integrations menu in the DevSkiller administration panel, and acquire your key in the API Access section.
More details about acquiring the API Key can be found in our help https://help.devskiller.com/space/TSG/2897543172/How+to+integrate+with+an+ATS
Rate Limits
Our API enforces rate limiting to ensure fair usage and stability of the service. The current rate limit configuration allows a maximum of 36 requests per 8-second period.
This means that if you make requests continuously, you are allowed to make a request approximately every 0.222 seconds. However, if you were to make 36 requests all at once, you would then need to wait 8 seconds before making the next request.
In case you exceed these limits, our API will return a 429 Too Many Requests
HTTP status code.
Please design your application to adhere to these rate limits for optimal performance and to avoid service disruptions.
Candidate operations
The Candidates resources is used to create and list candidates
Listing candidates
Request
GET /candidates?query=John&count=10&page=1&status=TOKEN_SENT&tags=Tag1&tags=Tag2 HTTP/1.1
X-Api-Key: TEST-API-KEY
Accept: application/json
Host: api.devskiller.com
$ curl 'https://api.devskiller.com/candidates?query=John&count=10&page=1&status=TOKEN_SENT&tags=Tag1&tags=Tag2' -i -X GET \
-H 'X-Api-Key: TEST-API-KEY' \
-H 'Accept: application/json'
Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 642
{
"items" : [ {
"id" : "CANDIDATE-UUID-1234",
"firstName" : "John",
"lastName" : "Smith",
"email" : "test@email.com",
"examId" : "UUID-FOR-123",
"status" : "TOKEN_SENT",
"scoredPoints" : null,
"maxPoints" : 65,
"examUrl" : "http://exam.url/exam.html?TEST-TOKEN",
"tags" : [ "Tag1", "Tag2" ],
"externalId" : "EXTERNAL-1234",
"creationDate" : "2023-05-14T14:40:39.00191Z",
"testTimedOut" : false,
"testStartDate" : "2023-05-15T14:40:39.00191Z",
"testFinishDate" : "2023-05-15T15:24:39.00191Z"
} ],
"totalElements" : 1,
"totalPages" : 1,
"pageSize" : 10,
"pageNumber" : 1
}
{
"items" : [ {
"id" : "CANDIDATE-UUID-1234",
"firstName" : "John",
"lastName" : "Smith",
"email" : "test@email.com",
"examId" : "UUID-FOR-123",
"status" : "TOKEN_SENT",
"scoredPoints" : null,
"maxPoints" : 65,
"examUrl" : "http://exam.url/exam.html?TEST-TOKEN",
"tags" : [ "Tag1", "Tag2" ],
"externalId" : "EXTERNAL-1234",
"creationDate" : "2023-05-14T14:40:39.00191Z",
"testTimedOut" : false,
"testStartDate" : "2023-05-15T14:40:39.00191Z",
"testFinishDate" : "2023-05-15T15:24:39.00191Z"
} ],
"totalElements" : 1,
"totalPages" : 1,
"pageSize" : 10,
"pageNumber" : 1
}
GET /candidates
A GET
request will list all candidates.
Request parameters:
query
Optional
|
Search by name,email or token |
status
Optional
|
Filter candidates by status. Add multiple status parameters to search for several statuses. |
tags
Optional
|
Filter candidates by tags. Add multiple tags parameters to search for several tags. |
count
Optional
|
Maximum number of candidates to get. Default: 10, Max: 100 |
page
Optional
|
Number of the page to fetch, starting with 1. Default: 1 |
Response structure:
pageNumber
Type: Number
Content: Standard
|
Number of the current page |
pageSize
Type: Number
Content: Standard
|
Maximum number of elements for a page |
totalElements
Type: Number
Content: Standard
|
Total number of candidates matching criteria |
totalPages
Type: Number
Content: Standard
|
Total number of pages matching criteria |
items
Type: Array
Content: Standard
|
An array with candidates |
items[].id
Type: String
Content: Standard
|
The candidate’s id |
items[].externalId
Type: String
Content: Standard
|
The candidate’s external id |
items[].firstName
Type: String
Content: Standard
|
The candidate’s first name |
items[].lastName
Type: String
Content: Standard
|
The candidate’s last name |
items[].email
Type: String
Content: Standard
|
The candidate’s email address |
items[].examId
Type: String
Content: Standard
|
The exam’s id that the candidate has been asked to solve |
items[].status
Type: String
Content: Standard
|
The candidate’s status. Possible values: TOKEN_SENT, TOKEN_EXPIRED, TEST_STARTED, TEST_FINISHED, AUTO_ASSESSMENT_READY, IN_ASSESSMENT, ASSESSMENT_READY, ACCEPTED, REJECTED |
items[].scoredPoints
Type: Null
Content: Standard
|
The candidate’s score. Null if candidate has not been assessed yet |
items[].maxPoints
Type: Number
Content: Standard
|
The maximum number of points, that candidate might get from assigned exam |
items[].examUrl
Type: String
Content: Standard
|
The url to the test for candidate |
items[].tags
Type: Array
Content: Standard
|
The candidate’s tags (array of strings) |
items[].creationDate
Type: String
Content: Standard
|
Creation date (ISO_8601) |
items[].testTimedOut
Type: Boolean
Content: Standard
|
Did the test time-out (true/false) |
items[].testStartDate
Type: String
Content: Standard
|
Test start date (ISO_8601) |
items[].testFinishDate
Type: String
Content: Standard
|
Test finish date (ISO_8601) |
Creating a candidate
Request
POST /candidates HTTP/1.1
Content-Type: application/json
X-Api-Key: TEST-API-KEY
Accept: application/json
Content-Length: 236
Host: api.devskiller.com
{
"communicationDisabled": false,
"firstName": "John",
"lastName": "Smith",
"examId": "UUID-FOR-123",
"tags": [
"Tag1",
"Tag2"
],
"email": "test@email.com",
"externalId": "EXTERNAL-1234"
}
$ curl 'https://api.devskiller.com/candidates' -i -X POST \
-H 'Content-Type: application/json' \
-H 'X-Api-Key: TEST-API-KEY' \
-H 'Accept: application/json' \
-d '{
"communicationDisabled": false,
"firstName": "John",
"lastName": "Smith",
"examId": "UUID-FOR-123",
"tags": [
"Tag1",
"Tag2"
],
"email": "test@email.com",
"externalId": "EXTERNAL-1234"
}'
Response
HTTP/1.1 201 Created
Location: https://api.devskiller.com/candidates/CANDIDATE-UUID-1234
Content-Type: application/json
Content-Length: 508
{
"id" : "CANDIDATE-UUID-1234",
"firstName" : "John",
"lastName" : "Smith",
"email" : "test@email.com",
"examId" : "UUID-FOR-123",
"status" : "TOKEN_SENT",
"scoredPoints" : null,
"maxPoints" : 65,
"examUrl" : "http://exam.url/exam.html?TEST-TOKEN",
"tags" : [ "Tag1", "Tag2" ],
"externalId" : "EXTERNAL-1234",
"creationDate" : "2023-05-14T14:40:39.00243Z",
"testTimedOut" : false,
"testStartDate" : "2023-05-15T14:40:39.00243Z",
"testFinishDate" : "2023-05-15T15:24:39.00243Z"
}
{
"id" : "CANDIDATE-UUID-1234",
"firstName" : "John",
"lastName" : "Smith",
"email" : "test@email.com",
"examId" : "UUID-FOR-123",
"status" : "TOKEN_SENT",
"scoredPoints" : null,
"maxPoints" : 65,
"examUrl" : "http://exam.url/exam.html?TEST-TOKEN",
"tags" : [ "Tag1", "Tag2" ],
"externalId" : "EXTERNAL-1234",
"creationDate" : "2023-05-14T14:40:39.00243Z",
"testTimedOut" : false,
"testStartDate" : "2023-05-15T14:40:39.00243Z",
"testFinishDate" : "2023-05-15T15:24:39.00243Z"
}
POST /candidates
A POST
request is used to create a candidate. After successfully creating a candidate, an email with the invitation will be send to the candidate.
Request fields:
email
Type: String
Required
|
The candidate's email address |
firstName
Type: String
Required
|
The candidate's first name |
lastName
Type: String
Required
|
The candidate's last name |
examId
Type: String
Required
|
The exam's id that the candidate has been asked to solve |
tags
Type: Array
Optional
|
The candidate's tags (array of strings) |
communicationDisabled
Type: Boolean
Optional
|
Should the outbound communication to the candidate be disabled. Default: "false" |
externalId
Type: String
Optional
|
Unique identifier of the candidate used in your system |
Possible response codes
200 OK
|
The request completed successfully |
400 Bad Request
|
The request was malformed. The response body will include an error providing further information |
412 Precondition failed
|
Requested exam cannot be used (it doesn't exist or is in wrong state) |
417 Expectation failed
|
There are insufficient credits to complete the request |
Details of a candidate
Request
GET /candidates/CANDIDATE-UUID-1234 HTTP/1.1
X-Api-Key: TEST-API-KEY
Accept: application/json
Host: api.devskiller.com
$ curl 'https://api.devskiller.com/candidates/CANDIDATE-UUID-1234' -i -X GET \
-H 'X-Api-Key: TEST-API-KEY' \
-H 'Accept: application/json'
Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 508
{
"id" : "CANDIDATE-UUID-1234",
"firstName" : "John",
"lastName" : "Smith",
"email" : "test@email.com",
"examId" : "UUID-FOR-123",
"status" : "TOKEN_SENT",
"scoredPoints" : null,
"maxPoints" : 65,
"examUrl" : "http://exam.url/exam.html?TEST-TOKEN",
"tags" : [ "Tag1", "Tag2" ],
"externalId" : "EXTERNAL-1234",
"creationDate" : "2023-05-14T14:40:39.00261Z",
"testTimedOut" : false,
"testStartDate" : "2023-05-15T14:40:39.00262Z",
"testFinishDate" : "2023-05-15T15:24:39.00262Z"
}
{
"id" : "CANDIDATE-UUID-1234",
"firstName" : "John",
"lastName" : "Smith",
"email" : "test@email.com",
"examId" : "UUID-FOR-123",
"status" : "TOKEN_SENT",
"scoredPoints" : null,
"maxPoints" : 65,
"examUrl" : "http://exam.url/exam.html?TEST-TOKEN",
"tags" : [ "Tag1", "Tag2" ],
"externalId" : "EXTERNAL-1234",
"creationDate" : "2023-05-14T14:40:39.00261Z",
"testTimedOut" : false,
"testStartDate" : "2023-05-15T14:40:39.00262Z",
"testFinishDate" : "2023-05-15T15:24:39.00262Z"
}
GET /candidates/{id}
A GET
request will retrieve the details of a candidate
Response structure
id
Type: String
Content: Standard
|
The candidate's id |
externalId
Type: String
Content: Standard
|
The candidate's external id |
status
Type: String
Content: Standard
|
The candidate's status. Possible values: TOKEN_SENT, TOKEN_EXPIRED, TEST_STARTED, TEST_FINISHED, AUTO_ASSESSMENT_READY, IN_ASSESSMENT, ASSESSMENT_READY, ACCEPTED, REJECTED, ERROR |
email
Type: String
Content: Standard
|
The candidate's email address |
firstName
Type: String
Content: Standard
|
The candidate's first name |
lastName
Type: String
Content: Standard
|
The candidate's last name |
scoredPoints
Type: Null
Content: Standard
|
The candidate's score. Null if candidate has not been assessed yet |
maxPoints
Type: Number
Content: Standard
|
The maximum number of points, that candidate might get from assigned exam |
examUrl
Type: String
Content: Standard
|
The url to the test for candidate |
examId
Type: String
Content: Standard
|
The exam's id that the candidate has been asked to solve |
tags
Type: Array
Content: Standard
|
The candidate's tags (array of strings) |
creationDate
Type: String
Content: Standard
|
Creation date (ISO_8601) |
testTimedOut
Type: Boolean
Content: Standard
|
Did the test time-out (true/false) |
testStartDate
Type: String
Content: Standard
|
Test start date (ISO_8601) |
testFinishDate
Type: String
Content: Standard
|
Test finish date (ISO_8601) |
Results for a candidate
Request
GET /candidates/CANDIDATE-UUID-5678/results HTTP/1.1
X-Api-Key: TEST-API-KEY
Accept: application/json
Host: api.devskiller.com
$ curl 'https://api.devskiller.com/candidates/CANDIDATE-UUID-5678/results' -i -X GET \
-H 'X-Api-Key: TEST-API-KEY' \
-H 'Accept: application/json'
Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 564
{
"reportUrl" : "https://report.devskiller.com/candidate-report.html?REPORT-UUID/REPORT-UUID2",
"pdfReportUrl" : null,
"scoredPoints" : 45,
"maxPoints" : 65,
"skills" : [ {
"name" : "Skill1",
"scoredPoints" : 50,
"maxPoints" : 100
}, {
"name" : "Skill2",
"scoredPoints" : 36,
"maxPoints" : 100
} ],
"note" : "Some note about candidate",
"askedForContractData" : true,
"financialExpectations" : 5000,
"noticePeriod" : "one month",
"replySent" : false,
"examDurationInMinutes" : 44,
"examTimeLimitInMinutes" : 60
}
{
"reportUrl" : "https://report.devskiller.com/candidate-report.html?REPORT-UUID/REPORT-UUID2",
"pdfReportUrl" : null,
"scoredPoints" : 45,
"maxPoints" : 65,
"skills" : [ {
"name" : "Skill1",
"scoredPoints" : 50,
"maxPoints" : 100
}, {
"name" : "Skill2",
"scoredPoints" : 36,
"maxPoints" : 100
} ],
"note" : "Some note about candidate",
"askedForContractData" : true,
"financialExpectations" : 5000,
"noticePeriod" : "one month",
"replySent" : false,
"examDurationInMinutes" : 44,
"examTimeLimitInMinutes" : 60
}
GET /candidates/{id}/results
A GET
request will retrieve the results for a candidate
Response structure
maxPoints
Type: Number
Content: Standard
|
Maximum points that candidate could get for the test |
scoredPoints
Type: Number
Content: Standard
|
The candidate's score |
examDurationInMinutes
Type: Number
Content: Standard
|
How long did it take to complete the test |
examTimeLimitInMinutes
Type: Number
Content: Standard
|
Exam's time limit |
askedForContractData
Type: Boolean
Content: Standard
|
True if candidate has been asked for contract data |
financialExpectations
Type: Number
Content: Standard
|
Financial expectations, available if candidate has been asked for contract data. |
noticePeriod
Type: String
Content: Standard
|
Notice period, available if candidate has been asked for contract data |
note
Type: String
Content: Standard
|
Optional note about candidate, made by assessor |
replySent
Type: Boolean
Content: Standard
|
True if a feedback mail has been sent to the candidate |
reportUrl
Type: String
Content: Standard
|
The candidate's report URL. |
pdfReportUrl
Type: Null
Content: Standard
|
The candidate's report URL (PDF download). |
skills
Type: Array
Content: Standard
|
The candidate's skills results. |
skills[].name
Type: String
Content: Standard
|
The skill's name. |
skills[].scoredPoints
Type: Number
Content: Standard
|
The skil's scored points. |
skills[].maxPoints
Type: Number
Content: Standard
|
The skill's max points. |
Exam operations
The Exams resources is used to list exams
List exams
Request
GET /exams?count=10&page=1 HTTP/1.1
X-Api-Key: TEST-API-KEY
Accept: application/json
Host: api.devskiller.com
$ curl 'https://api.devskiller.com/exams?count=10&page=1' -i -X GET \
-H 'X-Api-Key: TEST-API-KEY' \
-H 'Accept: application/json'
Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 558
{
"items" : [ {
"id" : "UUID-FOR-123",
"name" : "Test exam 123",
"durationInMinutes" : 100,
"automaticAssessmentPossible" : true,
"numberOfPages" : 2,
"numberOfTasks" : 4,
"skillTags" : [ "Java", "Hibernate" ]
}, {
"id" : "UUID-FOR-456",
"name" : "Test exam 456",
"durationInMinutes" : 100,
"automaticAssessmentPossible" : true,
"numberOfPages" : 2,
"numberOfTasks" : 4,
"skillTags" : [ "Java", "Hibernate" ]
} ],
"totalElements" : 2,
"totalPages" : 1,
"pageSize" : 10,
"pageNumber" : 1
}
{
"items" : [ {
"id" : "UUID-FOR-123",
"name" : "Test exam 123",
"durationInMinutes" : 100,
"automaticAssessmentPossible" : true,
"numberOfPages" : 2,
"numberOfTasks" : 4,
"skillTags" : [ "Java", "Hibernate" ]
}, {
"id" : "UUID-FOR-456",
"name" : "Test exam 456",
"durationInMinutes" : 100,
"automaticAssessmentPossible" : true,
"numberOfPages" : 2,
"numberOfTasks" : 4,
"skillTags" : [ "Java", "Hibernate" ]
} ],
"totalElements" : 2,
"totalPages" : 1,
"pageSize" : 10,
"pageNumber" : 1
}
GET /exams
A GET
request will list all candidates.
Request parameters:
count
Optional
|
Maximum number of exams to get. Default: 10, Max: 100 |
page
Optional
|
Number of the page to fetch, starting with 1. Default: 1 |
Response structure
pageNumber
Type: Number
Content: Standard
|
Number of the current page |
pageSize
Type: Number
Content: Standard
|
Maximum number of elements for a page |
totalElements
Type: Number
Content: Standard
|
Total number of exams matching criteria |
totalPages
Type: Number
Content: Standard
|
Total number of pages matching criteria |
items
Type: Array
Content: Standard
|
An array of exams |
items[].id
Type: String
Content: Standard
|
The exam’s id that the candidate has been asked to solve |
items[].name
Type: String
Content: Standard
|
The exam’s name |
items[].durationInMinutes
Type: Number
Content: Standard
|
How long did it take to complete the test |
items[].automaticAssessmentPossible
Type: Boolean
Content: Standard
|
Is automatic assessment of candidate’s answers is possible? If false, some of candidate’s answers needs manual assessment |
items[].numberOfPages
Type: Number
Content: Standard
|
The exam’s number of pages |
items[].numberOfTasks
Type: Number
Content: Standard
|
The exam’s number of tasks |
items[].skillTags
Type: Array
Content: Standard
|
Array of skills tested with this exam |
Details of an exam
Request
GET /exams/UUID-FOR-123 HTTP/1.1
X-Api-Key: TEST-API-KEY
Accept: application/json
Host: api.devskiller.com
$ curl 'https://api.devskiller.com/exams/UUID-FOR-123' -i -X GET \
-H 'X-Api-Key: TEST-API-KEY' \
-H 'Accept: application/json'
Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 211
{
"id" : "UUID-FOR-123",
"name" : "Test exam 123",
"durationInMinutes" : 100,
"automaticAssessmentPossible" : true,
"numberOfPages" : 2,
"numberOfTasks" : 4,
"skillTags" : [ "Java", "Hibernate" ]
}
{
"id" : "UUID-FOR-123",
"name" : "Test exam 123",
"durationInMinutes" : 100,
"automaticAssessmentPossible" : true,
"numberOfPages" : 2,
"numberOfTasks" : 4,
"skillTags" : [ "Java", "Hibernate" ]
}
GET /exams/{id}
A GET
request will retrieve the details of an exam
Response structure
id
Type: String
Content: Standard
|
The exam's id |
name
Type: String
Content: Standard
|
The exam's name |
durationInMinutes
Type: Number
Content: Standard
|
The exam's duration in minutes |
numberOfPages
Type: Number
Content: Standard
|
The exam's number of pages |
numberOfTasks
Type: Number
Content: Standard
|
The exam's number of tasks |
skillTags
Type: Array
Content: Standard
|
Array of skills tested with this exam |
automaticAssessmentPossible
Type: Boolean
Content: Standard
|
Is automatic assessment of candidate's answers is possible? If false, some of candidate's answers needs manual assessment |
Webhooks
Webhooks are inverted API endpoints which allows you to receive push notifications from DevSkiller.
To receive DevSkiller's push notifications you have to register your webhook endpoint address in the administration panel. You will also find here the webhook secret which is used to secure all requests sent by DevSkiller. The secret is always passed in the "X-Hook-Secret" header.
Receive push notification
Request
POST /devskillerWebhooks/ HTTP/1.1
Content-Type: application/json
X-Hook-Secret: TEST-SECRET
Content-Length: 299
Host: api.yourdomain.com
[
{
"candidateId": "CANDIDATE-UUID-1234",
"candidateExternalId": "EXTERNAL-1234",
"candidateEmail": "test@email.com",
"examId": "UUID-FOR-123",
"examName": "Test exam 123",
"status": "ASSESSMENT_COMPLETED",
"event": "EXAM_FINISHED"
}
]
$ curl 'https://api.yourdomain.com/devskillerWebhooks/' -i -X POST \
-H 'Content-Type: application/json' \
-H 'X-Hook-Secret: TEST-SECRET' \
-d '[
{
"candidateId": "CANDIDATE-UUID-1234",
"candidateExternalId": "EXTERNAL-1234",
"candidateEmail": "test@email.com",
"examId": "UUID-FOR-123",
"examName": "Test exam 123",
"status": "ASSESSMENT_COMPLETED",
"event": "EXAM_FINISHED"
}
]'
POST https://your_endpoint_address_here
A POST
request will push events to your endpoint.
If your endpoint responded 4xx or 5xx DevSkiller will retry the request with a given schedule:
- after 3 seconds
- after 15 seconds
- after 1 minute
- after 5 minutes
- after 30 minutes
- after 150 minutes
Request headers
Name | Description |
---|---|
X-Hook-Secret |
The webhook secret key |
Request structure
[]
Type: Array
Always
|
An array of events |
[].candidateId
Type: String
Always
|
The candidate's id |
[].candidateExternalId
Type: String
If used
|
The candidate's external id |
[].candidateEmail
Type: String
Always
|
The candidate's email |
[].examId
Type: String
Always
|
The id of the exam |
[].examName
Type: String
Always
|
The name of the exam |
[].event
Type: String
Always
|
Event type. Possible values: EXAM_FINISHED, EXAM_STARTED, TOKEN_EXPIRED, ERROR |
[].status
Type: String
Always
|
Event type status. Possible values: WAITING_FOR_ASSESSMENT, EXAM_STARTED, ASSESSMENT_COMPLETED, TOKEN_EXPIRED, ERROR |
Errors
Response
HTTP/1.1 400 Bad Request
Content-Type: application/json
Content-Length: 249
{
"code" : 400,
"errorId" : "6d77c358-a91a-4360-8c2f-568cbace8130",
"details" : "Validation error",
"validationErrors" : [ {
"field" : "email",
"value" : "incorrect mail",
"message" : "must be a well-formed email address"
} ]
}
{
"code" : 400,
"errorId" : "6d77c358-a91a-4360-8c2f-568cbace8130",
"details" : "Validation error",
"validationErrors" : [ {
"field" : "email",
"value" : "incorrect mail",
"message" : "must be a well-formed email address"
} ]
}
Whenever an error response (status code >= 400) is returned, the body will contain a JSON object that describes the problem. The error object has the following structure:
code
Type: Number
Content: Standard
|
Error code |
errorId
Type: String
Content: Standard
|
Unique error id, you can use it when contacting with us |
details
Type: String
Content: Standard
|
Error description |
validationErrors
Type: Array
Content: Standard
|
Validation errors details |
validationErrors[].field
Type: String
Content: Standard
|
Name of the field |
validationErrors[].value
Type: String
Content: Standard
|
Rejected value |
validationErrors[].message
Type: String
Content: Standard
|
Message |
For example, a request that attempts to apply a non-existent tag to a note will produce a
400 Bad Request
response