Fetch API
curl --request POST \
--url https://api.interviewflowai.com/api/external/fetch/candidates \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: <api-key>' \
--data '
{
"jobId": "<string>",
"page": 123,
"limit": 123,
"interviewId": "<string>",
"interviewStatus": "<string>",
"additionalFields": {},
"tagFilter": {}
}
'import requests
url = "https://api.interviewflowai.com/api/external/fetch/candidates"
payload = {
"jobId": "<string>",
"page": 123,
"limit": 123,
"interviewId": "<string>",
"interviewStatus": "<string>",
"additionalFields": {},
"tagFilter": {}
}
headers = {
"X-API-KEY": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-KEY': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
jobId: '<string>',
page: 123,
limit: 123,
interviewId: '<string>',
interviewStatus: '<string>',
additionalFields: {},
tagFilter: {}
})
};
fetch('https://api.interviewflowai.com/api/external/fetch/candidates', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.interviewflowai.com/api/external/fetch/candidates",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'jobId' => '<string>',
'page' => 123,
'limit' => 123,
'interviewId' => '<string>',
'interviewStatus' => '<string>',
'additionalFields' => [
],
'tagFilter' => [
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-KEY: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.interviewflowai.com/api/external/fetch/candidates"
payload := strings.NewReader("{\n \"jobId\": \"<string>\",\n \"page\": 123,\n \"limit\": 123,\n \"interviewId\": \"<string>\",\n \"interviewStatus\": \"<string>\",\n \"additionalFields\": {},\n \"tagFilter\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-KEY", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.interviewflowai.com/api/external/fetch/candidates")
.header("X-API-KEY", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"jobId\": \"<string>\",\n \"page\": 123,\n \"limit\": 123,\n \"interviewId\": \"<string>\",\n \"interviewStatus\": \"<string>\",\n \"additionalFields\": {},\n \"tagFilter\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.interviewflowai.com/api/external/fetch/candidates")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-KEY"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"jobId\": \"<string>\",\n \"page\": 123,\n \"limit\": 123,\n \"interviewId\": \"<string>\",\n \"interviewStatus\": \"<string>\",\n \"additionalFields\": {},\n \"tagFilter\": {}\n}"
response = http.request(request)
puts response.read_bodyAPI Integration
Fetch API
Retrieve candidate lists, specific interview details, and filter records directly from InterviewFlowAI.
POST
/
api
/
external
/
fetch
/
candidates
Fetch API
curl --request POST \
--url https://api.interviewflowai.com/api/external/fetch/candidates \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: <api-key>' \
--data '
{
"jobId": "<string>",
"page": 123,
"limit": 123,
"interviewId": "<string>",
"interviewStatus": "<string>",
"additionalFields": {},
"tagFilter": {}
}
'import requests
url = "https://api.interviewflowai.com/api/external/fetch/candidates"
payload = {
"jobId": "<string>",
"page": 123,
"limit": 123,
"interviewId": "<string>",
"interviewStatus": "<string>",
"additionalFields": {},
"tagFilter": {}
}
headers = {
"X-API-KEY": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-KEY': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
jobId: '<string>',
page: 123,
limit: 123,
interviewId: '<string>',
interviewStatus: '<string>',
additionalFields: {},
tagFilter: {}
})
};
fetch('https://api.interviewflowai.com/api/external/fetch/candidates', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.interviewflowai.com/api/external/fetch/candidates",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'jobId' => '<string>',
'page' => 123,
'limit' => 123,
'interviewId' => '<string>',
'interviewStatus' => '<string>',
'additionalFields' => [
],
'tagFilter' => [
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-KEY: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.interviewflowai.com/api/external/fetch/candidates"
payload := strings.NewReader("{\n \"jobId\": \"<string>\",\n \"page\": 123,\n \"limit\": 123,\n \"interviewId\": \"<string>\",\n \"interviewStatus\": \"<string>\",\n \"additionalFields\": {},\n \"tagFilter\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-KEY", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.interviewflowai.com/api/external/fetch/candidates")
.header("X-API-KEY", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"jobId\": \"<string>\",\n \"page\": 123,\n \"limit\": 123,\n \"interviewId\": \"<string>\",\n \"interviewStatus\": \"<string>\",\n \"additionalFields\": {},\n \"tagFilter\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.interviewflowai.com/api/external/fetch/candidates")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-KEY"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"jobId\": \"<string>\",\n \"page\": 123,\n \"limit\": 123,\n \"interviewId\": \"<string>\",\n \"interviewStatus\": \"<string>\",\n \"additionalFields\": {},\n \"tagFilter\": {}\n}"
response = http.request(request)
puts response.read_bodyThe Fetch API allows you to pull candidate and interview data back into your own internal systems. All fetch requests are
The request body does not accept fields other than those listed above. Keys in
This example shows every field that can be returned. Fields without a value are omitted, except nullable fields such as
POST requests with a JSON body.
Rate limit: 60 requests per minute per API key. Requests over the limit return
429 Too Many Requests.Request body fields
string
required
The unique ID of the Interviewer to fetch candidates from.
integer
default:"1"
Page number. Minimum
1.integer
default:"20"
Results per page. Minimum
1 and maximum 100.string
Fetch a single Candidate by their unique interview ID.
string
Filter Candidates by interview status.
object
Filter by one or more custom fields. Values can be strings, numbers, or booleans.
object
Filter by one or more tags. Values can be strings, numbers, booleans, or non-empty arrays of those types.
additionalFields and tagFilter must contain at least one non-whitespace character, and each object must contain at least one property.
1. Paginated fetch
Retrieve a paginated list of candidates for a specific Interviewer.curl -X POST "https://api.interviewflowai.com/api/external/fetch/candidates" \
-H "Content-Type: application/json" \
-H "X-API-KEY: your_api_key_here" \
-d '{
"jobId": "9786b00b-d110-454f-9e6f-1732daec8916",
"page": 1,
"limit": 20
}'
2. Single candidate fetch
Look up a specific candidate using their unique interview ID.curl -X POST "https://api.interviewflowai.com/api/external/fetch/candidates" \
-H "Content-Type: application/json" \
-H "X-API-KEY: your_api_key_here" \
-d '{
"jobId": "9786b00b-d110-454f-9e6f-1732daec8916",
"interviewId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
}'
3. Custom field filters
Filter your candidate list based on values inadditionalFields. Filtering rules:
- Numeric values: Returns candidates where the field value is ≥ the specified number (e.g.
"score": 50returns all candidates with score 50 or above). - String and boolean values: Returns candidates where the field value exactly matches the supplied value.
curl -X POST "https://api.interviewflowai.com/api/external/fetch/candidates" \
-H "Content-Type: application/json" \
-H "X-API-KEY: your_api_key_here" \
-d '{
"jobId": "9786b00b-d110-454f-9e6f-1732daec8916",
"page": 1,
"limit": 20,
"additionalFields": {
"score": 50,
"review": "yes"
}
}'
4. Status and tag filters
curl -X POST "https://api.interviewflowai.com/api/external/fetch/candidates" \
-H "Content-Type: application/json" \
-H "X-API-KEY: your_api_key_here" \
-d '{
"jobId": "9786b00b-d110-454f-9e6f-1732daec8916",
"interviewStatus": "completed",
"tagFilter": {
"department": ["Engineering", "Product"],
"requiresReview": true
}
}'
Response schema
{
"data": {
"interviews": [
{
"starRating": null,
"id": "480f9248-39f2-4e48-afb7-5925e8f8c07e",
"jobId": "9786b00b-d110-454f-9e6f-1732daec8916",
"candidateName": "Jane Doe",
"candidateEmail": "jane@example.com",
"source": "api",
"interviewType": "web_call",
"phoneNumber": null,
"status": "completed",
"interviewScore": 82,
"resumeFeedback": {
"candidate_name": "Jane Doe",
"criteria_results": [
{
"score": 4,
"reason": "The Candidate has relevant experience.",
"weight": 5,
"criterion_name": "Relevant experience"
}
],
"dealbreaker_results": [
{
"reason": "The Candidate meets the requirement.",
"status": "Pass",
"dealbreaker_text": "Minimum required experience"
}
],
"overall_score_percentage": 75,
"overall_dealbreaker_status": "Pass"
},
"portFolioFeedback": {
"overall_score_100": 70
},
"portfolioUrl": "https://example.com/portfolio",
"transcript": "20-AI Interviewer: ...",
"recordingUrl": "https://example.com/recording.mp4",
"resume_score": 75,
"portfolio_score": 70,
"interview_feedback": {
"job_title": "Account Executive",
"red_flags": [],
"tag_answers": [
{
"value": ["5+"],
"tag_name": "Years Of Experience"
}
],
"recommendation": "yes",
"overall_score_100": 82,
"strengths_timestamps": [
"128s: The Candidate gave a specific example."
],
"communication_assessment": {
"grammar_and_syntax": {
"score": 4,
"feedback": "The Candidate used clear sentence structures."
},
"conversational_flow": {
"score": 4,
"feedback": "The Candidate maintained a coherent conversation."
},
"vocabulary_and_expression": {
"score": 4,
"feedback": "The Candidate used precise professional language."
},
"communication_effectiveness": {
"score": 4,
"feedback": "The Candidate explained their experience clearly."
}
},
"risks_or_gaps_timestamps": [],
"notes_for_human_interviewer": [],
"custom_questions_with_answers_timestamps": [
{
"score": 4,
"answer": "I reviewed the pipeline weekly and prioritized high-intent opportunities.",
"question": "How do you prioritize your pipeline?",
"reasoning": "The answer describes a clear process and relevant actions.",
"timestamp": "128s:",
"weight_used": 5
}
]
},
"customFields": [
{
"id": "screening_question_id",
"type": "yesno",
"answer": "Yes",
"question": "Are you legally authorized to work in this country?"
}
],
"shortlistTimeInMillis": 1786341452787,
"interviewExpiryTimeInMillis": 1786946252787,
"recruiterNotes": "Strong communication skills.",
"csvMetaData": {},
"trustScoreMetadata": {},
"tagAnswers": [
{
"tag_name": "Years Of Experience",
"value": ["5+"]
}
],
"postInterviewAnswers": [
{
"answer": "https://example.com/submission",
"question": "Submit your work sample",
"questionId": "screening_question_id"
}
],
"retakeCount": 1,
"candidateAdditionalFields": {
"location": "New York"
},
"createdAt": "2026-08-07T05:52:44.794684+00:00",
"updatedAt": "2026-08-10T05:57:32.78728+00:00",
"retakeAttempts": [
{
"id": "bbbbbbbb-cccc-dddd-eeee-ffffffffffff",
"interviewId": "480f9248-39f2-4e48-afb7-5925e8f8c07e",
"jobId": "9786b00b-d110-454f-9e6f-1732daec8916",
"candidateName": "Jane Doe",
"candidateEmail": "jane@example.com",
"attemptNumber": 1,
"status": "completed",
"overall_score_100": 85,
"interview_feedback": {
"overall_score_100": 85
},
"customQuestions": [],
"transcript": "20-AI Interviewer: ...",
"originalTranscript": "20-AI Interviewer: ...",
"recordingUrl": "https://example.com/retake-recording.mp4",
"customFields": {},
"trustScoreMetadata": {},
"tagAnswers": [],
"completedAt": "2026-08-10T05:50:00.000000+00:00",
"createdAt": "2026-08-10T05:30:00.000000+00:00",
"updatedAt": "2026-08-10T05:57:32.78728+00:00"
}
]
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 22,
"totalPages": 2,
"hasNextPage": true,
"hasPreviousPage": false
}
},
"code": 200,
"message": "Interviews fetched successfully"
}
starRating and phoneNumber. tagAnswers can be an object or an array depending on the stored tag data. retakeAttempts is included only when you provide interviewId.
The top-level code contains the HTTP-equivalent status code. The message describes the result. The data.interviews array contains Candidate records, and data.pagination describes the current result page.
Feedback fields depend on the Candidate’s progress. For example,
interviewScore, transcript, recordingUrl, and interview_feedback are normally available after the interview is complete. shortlistTimeInMillis, interviewExpiryTimeInMillis, csvMetaData, postInterviewAnswers, and retakeAttempts appear only when relevant.
