Get asynchronous job status
curl --request GET \
--url https://api.runpulse.com/job/{jobId} \
--header 'x-api-key: <api-key>'import requests
url = "https://api.runpulse.com/job/{jobId}"
headers = {"x-api-key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'x-api-key': '<api-key>'}};
fetch('https://api.runpulse.com/job/{jobId}', 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.runpulse.com/job/{jobId}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"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"
"net/http"
"io"
)
func main() {
url := "https://api.runpulse.com/job/{jobId}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("x-api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.runpulse.com/job/{jobId}")
.header("x-api-key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.runpulse.com/job/{jobId}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["x-api-key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"job_id": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"result": {},
"error": "<string>"
}Jobs, Results & Webhooks
Poll Job
Check the status and retrieve results of an asynchronous job
(submitted via any endpoint with async: true).
GET
/
job
/
{jobId}
Get asynchronous job status
curl --request GET \
--url https://api.runpulse.com/job/{jobId} \
--header 'x-api-key: <api-key>'import requests
url = "https://api.runpulse.com/job/{jobId}"
headers = {"x-api-key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'x-api-key': '<api-key>'}};
fetch('https://api.runpulse.com/job/{jobId}', 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.runpulse.com/job/{jobId}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"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"
"net/http"
"io"
)
func main() {
url := "https://api.runpulse.com/job/{jobId}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("x-api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.runpulse.com/job/{jobId}")
.header("x-api-key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.runpulse.com/job/{jobId}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["x-api-key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"job_id": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"result": {},
"error": "<string>"
}Overview
Check the status and retrieve results of an asynchronous job (e.g., submitted via/extract with async: true).
Poll this endpoint periodically until the job reaches a terminal state (completed, failed, canceled, or expired).
Response
The response includes job metadata and, when completed, the full extraction results.{
"job_id": "abc123-def456-ghi789",
"status": "completed",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:31:45Z",
"result": {
"markdown": "# Document Title\n\nExtracted content...",
"page_count": 15,
"bounding_boxes": { ... },
"plan-info": { ... }
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
job_id | string | Unique identifier for the extraction job. |
status | string | Current job status: pending, processing, completed, failed, canceled, or expired. |
created_at | string | ISO 8601 timestamp when the job was submitted. |
updated_at | string | ISO 8601 timestamp of the last status update. |
result | object | Job output. Present when status is completed, and on expired as a retention stub. Large outputs return a { is_url, url } pointer instead of inline data (see Large Results). See Extract for result structure. |
error | string | Error message (only present when status is failed). |
Job Status Values
| Status | Description |
|---|---|
pending | Job is queued and waiting to be processed. |
processing | Job is currently being processed. |
completed | Job finished successfully. Results are available in the result field. |
failed | Job encountered an error. See error field for details. |
canceled | Job was canceled before completion. |
expired | Job’s retention window has passed and its stored output was purged. Run a new extraction to regenerate it. |
Large Results (is_url)
When the output is large (at or above 5 MB), or when a spreadsheet extraction uses default URL delivery, result is replaced with a pointer instead of inline data. Fetch url to download the full result JSON; the downloaded payload has the normal result shape.
{
"result": {
"is_url": true,
"url": "https://api.runpulse.com/results/abc123-def456-ghi789"
}
}
| Field | Type | Description |
|---|---|---|
is_url | boolean | true when result is a pointer rather than inline data. |
url | string | Location of the full result JSON (presigned S3 URL or Pulse proxy URL). |
Polling Strategy
We recommend polling with exponential backoff:from pulse import Pulse
import time
client = Pulse(api_key="YOUR_API_KEY")
def poll_job(job_id: str, max_attempts: int = 60):
"""
Poll for job completion with exponential backoff.
Args:
job_id: The job ID returned from /extract with async: true
max_attempts: Maximum number of polling attempts
Returns:
The extraction result when job completes
"""
delay = 1 # Start with 1 second
for attempt in range(max_attempts):
# Get job status using the SDK
response = client.jobs.get_job(job_id=job_id)
if response.status == "completed":
return response.result
elif response.status == "failed":
raise Exception(f"Job failed: {response.error}")
elif response.status == "canceled":
raise Exception("Job was canceled")
# Still pending or processing - wait and retry
print(f"Status: {response.status}, waiting {delay}s...")
time.sleep(delay)
delay = min(delay * 1.5, 10) # Cap at 10 seconds
raise Exception("Polling timeout")
# Example usage
job_id = "abc123-def456-ghi789"
result = poll_job(job_id)
print(f"Extraction complete! Markdown: {result['markdown'][:100]}...")
import { PulseClient } from 'pulse-ts-sdk';
const client = new PulseClient({
apiKey: 'YOUR_API_KEY'
});
async function pollJob(jobId: string, maxAttempts: number = 60): Promise<any> {
/**
* Poll for job completion with exponential backoff.
*/
let delay = 1; // Start with 1 second
for (let attempt = 0; attempt < maxAttempts; attempt++) {
// Get job status using the SDK
const response = await client.jobs.getJob({ jobId });
if (response.status === "completed") {
return response.result;
} else if (response.status === "failed") {
throw new Error(`Job failed: ${response.error}`);
} else if (response.status === "canceled") {
throw new Error("Job was canceled");
}
// Still pending or processing - wait and retry
console.log(`Status: ${response.status}, waiting ${delay}s...`);
await new Promise(resolve => setTimeout(resolve, delay * 1000));
delay = Math.min(delay * 1.5, 10); // Cap at 10 seconds
}
throw new Error("Polling timeout");
}
// Example usage
const jobId = "abc123-def456-ghi789";
pollJob(jobId).then(result => {
console.log(`Extraction complete! Markdown: ${result.markdown?.slice(0, 100)}...`);
});
#!/bin/bash
JOB_ID="abc123-def456-ghi789"
API_KEY="YOUR_API_KEY"
MAX_ATTEMPTS=60
DELAY=1
for ((attempt=1; attempt<=MAX_ATTEMPTS; attempt++)); do
response=$(curl -s "https://api.runpulse.com/job/${JOB_ID}" \
-H "x-api-key: ${API_KEY}")
status=$(echo "$response" | jq -r '.status')
case "$status" in
"completed")
echo "Job completed!"
echo "$response" | jq '.result'
exit 0
;;
"failed")
error=$(echo "$response" | jq -r '.error')
echo "Job failed: $error"
exit 1
;;
"canceled")
echo "Job was canceled"
exit 1
;;
*)
echo "Status: $status, waiting ${DELAY}s... (attempt $attempt)"
sleep $DELAY
DELAY=$(echo "$DELAY * 1.5" | bc)
if (( $(echo "$DELAY > 10" | bc -l) )); then
DELAY=10
fi
;;
esac
done
echo "Polling timeout"
exit 1
Example Usage
Check Job Status
from pulse import Pulse
client = Pulse(api_key="YOUR_API_KEY")
# Check job status
job_id = "abc123-def456-ghi789"
response = client.jobs.get_job(job_id=job_id)
print(f"Job ID: {response.job_id}")
print(f"Status: {response.status}")
print(f"Created: {response.created_at}")
if response.status == "completed":
print(f"Markdown: {response.result['markdown'][:100]}...")
elif response.status == "failed":
print(f"Error: {response.error}")
import { PulseClient } from 'pulse-ts-sdk';
const client = new PulseClient({
apiKey: 'YOUR_API_KEY'
});
// Check job status
const jobId = "abc123-def456-ghi789";
const response = await client.jobs.getJob({ jobId });
console.log(`Job ID: ${response.job_id}`);
console.log(`Status: ${response.status}`);
console.log(`Created: ${response.created_at}`);
if (response.status === "completed") {
console.log(`Markdown: ${response.result?.markdown?.slice(0, 100)}...`);
} else if (response.status === "failed") {
console.log(`Error: ${response.error}`);
}
# Check job status
curl https://api.runpulse.com/job/abc123-def456-ghi789 \
-H "x-api-key: YOUR_API_KEY"
Complete Async Workflow
from pulse import Pulse
import time
import json
client = Pulse(api_key="YOUR_API_KEY")
# Step 1: Submit async extraction
print("Submitting async extraction...")
submit_response = client.extract(
file_url="https://platform.runpulse.com/api/examples/637e5678-30b1-45fa-acc4-877f2d636419/pdf",
async_=True
)
job_id = submit_response.job_id
print(f"Job submitted: {job_id}")
# Step 2: Poll for completion
delay = 1
while True:
status_response = client.jobs.get_job(job_id=job_id)
if status_response.status == "completed":
print("Extraction complete!")
extraction_id = status_response.result["extraction_id"]
print(f"Extraction ID: {extraction_id}")
break
elif status_response.status in ["failed", "canceled"]:
print(f"Job ended with status: {status_response.status}")
break
print(f"Status: {status_response.status}")
time.sleep(delay)
delay = min(delay * 1.5, 10)
# Step 3 (optional): Apply schema via /schema endpoint
schema_result = client.schema(
extraction_id=extraction_id,
schema_config={
"input_schema": {
"type": "object",
"properties": {
"account_holder": {"type": "string"},
"balance": {"type": "number"}
}
}
}
)
print(f"Schema output: {schema_result.schema_output}")
import { PulseClient } from 'pulse-ts-sdk';
const client = new PulseClient({
apiKey: 'YOUR_API_KEY'
});
// Step 1: Submit async extraction
console.log("Submitting async extraction...");
const submitResponse = await client.extract({
fileUrl: "https://platform.runpulse.com/api/examples/637e5678-30b1-45fa-acc4-877f2d636419/pdf",
async: true
});
const jobId = submitResponse.job_id;
console.log(`Job submitted: ${jobId}`);
// Step 2: Poll for completion
let delay = 1;
let extractionId: string;
while (true) {
const statusResponse = await client.jobs.getJob({ jobId });
if (statusResponse.status === "completed") {
console.log("Extraction complete!");
extractionId = statusResponse.result?.extraction_id;
console.log(`Extraction ID: ${extractionId}`);
break;
} else if (statusResponse.status === "failed" || statusResponse.status === "canceled") {
console.log(`Job ended with status: ${statusResponse.status}`);
break;
}
console.log(`Status: ${statusResponse.status}`);
await new Promise(resolve => setTimeout(resolve, delay * 1000));
delay = Math.min(delay * 1.5, 10);
}
// Step 3 (optional): Apply schema via /schema endpoint
const schemaResult = await client.schema({
extraction_id: extractionId,
schema_config: {
input_schema: {
type: "object",
properties: {
account_holder: { type: "string" },
balance: { type: "number" }
}
}
}
});
console.log(`Schema output:`, schemaResult.schema_output);
# Step 1: Submit async extraction
JOB_RESPONSE=$(curl -s -X POST https://api.runpulse.com/extract \
-H "x-api-key: YOUR_API_KEY" \
-F "file_url=https://platform.runpulse.com/api/examples/637e5678-30b1-45fa-acc4-877f2d636419/pdf" \
-F "async=true")
JOB_ID=$(echo "$JOB_RESPONSE" | jq -r '.job_id')
echo "Job submitted: $JOB_ID"
# Step 2: Poll for completion
DELAY=1
while true; do
STATUS_RESPONSE=$(curl -s "https://api.runpulse.com/job/${JOB_ID}" \
-H "x-api-key: YOUR_API_KEY")
STATUS=$(echo "$STATUS_RESPONSE" | jq -r '.status')
case "$STATUS" in
"completed")
echo "Extraction complete!"
EXTRACTION_ID=$(echo "$STATUS_RESPONSE" | jq -r '.result.extraction_id')
echo "Extraction ID: $EXTRACTION_ID"
break
;;
"failed"|"canceled")
echo "Job ended with status: $STATUS"
break
;;
*)
echo "Status: $STATUS"
sleep $DELAY
DELAY=$(echo "$DELAY * 1.5" | bc)
if (( $(echo "$DELAY > 10" | bc -l) )); then
DELAY=10
fi
;;
esac
done
For webhook-based notifications instead of polling, see the Webhooks documentation.
Authorizations
Path Parameters
Identifier returned from an async job submission.
Response
Current job status payload
Current status and metadata for an asynchronous job.
Identifier assigned to the asynchronous job.
Lifecycle status for an asynchronous job.
Available options:
pending, processing, completed, failed, canceled Timestamp when the job was accepted.
Timestamp of the last status update, if available.
Structured payload returned when the job completes. For large extractions and spreadsheet default URL delivery this object contains is_url: true and a single-use url to download the full result via GET /results/{jobId}.
Error message describing why the job failed, if applicable.
⌘I