Create a batch job
Submit up to 1,000 mixed enrich and operation items as one asynchronous job.
https://api.up2data.ai/v1/batchEvery /v1 capability is batchable. Mix profile, company, and post enrich items ({ "type": "profile" | "company" | "post", "url": "..." }) freely with operation items ({ "operation": "...", "body": {...} }), where body is the same shape as that operation's own sync request body. page, per_page, and pagination_token are rejected everywhere in a batch item's body - batch paginates through everything up to max_results on its own, instead of one page per call. search.people.sales_nav and search.companies.sales_nav still take filters or sales_nav_url, exactly one, same as their sync endpoints. search.jobs requires at least one of company_url, keywords, or filters in a batch item - unlike the sync endpoint, an empty search.jobs item is rejected with 400 instead of returning a free empty result. Deliver results by webhook_url (the batch.completed event) and/or by polling.
Every item reserves its worst case at submission. A profile, company, post, or Sales Navigator search item is charged in full up front and never refunds for a failed item - the only refund on those is the unused part of a Sales Navigator search item's max_results reservation, once that search runs and comes back short. Every other operation also reserves the worst case at submission, but settles down to what it actually billed once its queue finishes: a 422 unprocessable_target failure, or a company or job that isn't found, costs 0 and refunds in full - the same as an empty page or an empty direction/category. See the table below for the exact rate of each operation, matching its sync endpoint.
| Operation | Sync twin | max_results (default / max) |
Billing | Result type |
|---|---|---|---|---|
profile (enrich) |
Enrich a profile | not accepted | 1 credit flat, charged in full at submission, never refunded | profile |
company (enrich) |
Enrich a company | not accepted | 1 credit flat, never refunded | company |
post (enrich) |
Enrich a post | not accepted | 1 credit flat, never refunded | post |
profiles.activity |
Get profile activity | 100 / 500 | Reserved: 2 credits per 25-row page (ceil(max_results / 25) pages). Billed: the same rate on rows actually returned, minimum 1 page. |
activity_post, activity_comment, or activity_reaction (matches kind) |
profiles.recommendations |
Get profile recommendations | not accepted | Reserved: 1 credit per requested direction (both reserves 2). Billed: 1 credit per direction that returned at least one recommendation; an empty direction is free. |
recommendation |
profiles.interests |
Get profile interests | not accepted | Reserved: 1 credit per requested category (all reserves 5). Billed: 1 credit per non-empty category. |
interest |
companies.jobs_count |
Count company job openings | not accepted | 1 credit, reserved and billed only if the company is found; 0 otherwise. | jobs_count |
companies.posts |
Get company posts | 100 / 500 | Reserved: 1 credit per max_results (flat, not paginated). Billed: 1 credit per post actually returned. |
company_post |
companies.headcount |
Find custom headcount | not accepted | 1 credit, reserved and billed only if the company is found; 0 otherwise. | headcount |
jobs.enrich |
Enrich a job | not accepted | 1 credit, reserved and billed only if the job is found; 0 otherwise. | job |
posts.engagement.comments |
Get post comments | 100 / 1,000 | Reserved: 1 credit per max_results (flat). Billed: 1 credit per comment actually returned. |
comment |
posts.engagement.reactions |
Get post reactions | 100 / 1,000 | Reserved: 1 credit per max_results (flat). Billed: 1 credit per reaction actually returned. |
reaction |
posts.engagement.reposts |
Get post reposts | 100 / 1,000 | Reserved: 1 credit per max_results (flat). Billed: 1 credit per repost actually returned. |
repost |
search.people.sales_nav |
Search people (Sales Navigator) | 100 / 2,500 | Reserved: 1 credit per max_results (flat). Billed: 0 - refunds the unused part of the reservation once the search runs and returns fewer than max_results. |
profile |
search.companies.sales_nav |
Search companies (Sales Navigator) | 100 / 1,000 | Same reserve-and-refund model as search.people.sales_nav. |
company |
search.people.standard |
Search people (standard) | 100 / 2,500 | Reserved: 1 credit per 25-row page (ceil(max_results / 25)). Billed: 1 credit per page of rows actually returned; 0 when empty. |
person |
search.companies.standard |
Search companies (standard) | 100 / 1,000 | Same page-based reserve/bill as search.people.standard. |
company |
search.jobs |
Search jobs | 100 / 500 | Reserved: 1 credit per max_results (flat, matching the sync per-job rate). Billed: 1 credit per job actually returned; 0 when empty. |
job |
search.posts |
Search posts | 100 / 500 | Reserved: 1 credit per 25-row page. Billed: 1 credit per page of rows actually returned; 0 when empty. | post |
A profile, company, post, or Sales Navigator search item is billed in full at submission and never refunds for a failed item - including unprocessable_target and retryable errors like 429, 500, or 504. The only refund on those is the unused part of a Sales Navigator search item's max_results reservation, credited back once that search runs. Every other operation item settles down to what it actually billed once its own queue finishes, so a failed or empty item on those costs 0 and refunds in full.
Batch submissions accept an optional Idempotency-Key header, so a retried submission with the same key returns the original job instead of creating a duplicate or charging twice. See Batch jobs.
#Request
| Field | Type | Notes |
|---|---|---|
itemsrequired | object[] | Up to 1,000 items, freely mixed. |
items[].type | string | Enrich item. Enum: profile, company, post. Required with url. |
items[].url | string | Required with type. |
items[].operation | string | Operation item. Enum: profiles.activity, profiles.recommendations, profiles.interests, companies.jobs_count, companies.posts, companies.headcount, jobs.enrich, posts.engagement.comments, posts.engagement.reactions, posts.engagement.reposts, search.people.sales_nav, search.companies.sales_nav, search.people.standard, search.companies.standard, search.jobs, search.posts. Required with body. |
items[].body | object | Request body for the operation: the same shape as that operation's own sync request body. page, per_page, and pagination_token are rejected. A scalar operation (companies.jobs_count, companies.headcount, jobs.enrich) rejects max_results entirely; every other operation accepts it with its own default and cap - see the table below. search.people.sales_nav and search.companies.sales_nav take filters or sales_nav_url, exactly one, same as their sync endpoints. search.jobs requires at least one of company_url, keywords, or filters. Required with operation. |
webhook_url | string | HTTPS endpoint for the batch.completed event. Omit for polling-only. |
Idempotency-Key | string (header) | Optional. A retried submission with the same key returns the original job; no duplicate is created. |
#Call it
curl -X POST "https://api.up2data.ai/v1/batch" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"items": [
{
"type": "profile",
"url": "https://linkedin.com/in/a"
},
{
"type": "profile",
"url": "https://linkedin.com/in/b"
},
{
"type": "company",
"url": "https://linkedin.com/company/acme"
},
{
"operation": "posts.engagement.comments",
"body": {
"urn": "7267273010393358336",
"max_results": 500
}
}
],
"webhook_url": "https://app.example.com/hooks/up2data"
}'import requests
url = "https://api.up2data.ai/v1/batch"
headers = {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
}
payload = {
"items": [
{
"type": "profile",
"url": "https://linkedin.com/in/a"
},
{
"type": "profile",
"url": "https://linkedin.com/in/b"
},
{
"type": "company",
"url": "https://linkedin.com/company/acme"
},
{
"operation": "posts.engagement.comments",
"body": {
"urn": "7267273010393358336",
"max_results": 500
}
}
],
"webhook_url": "https://app.example.com/hooks/up2data"
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())const url = "https://api.up2data.ai/v1/batch";
const headers = {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
};
const body = {
"items": [
{
"type": "profile",
"url": "https://linkedin.com/in/a"
},
{
"type": "profile",
"url": "https://linkedin.com/in/b"
},
{
"type": "company",
"url": "https://linkedin.com/company/acme"
},
{
"operation": "posts.engagement.comments",
"body": {
"urn": "7267273010393358336",
"max_results": 500
}
}
],
"webhook_url": "https://app.example.com/hooks/up2data"
};
const response = await fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
});
const data = await response.json();
console.log(data);package main
import (
"bytes"
"fmt"
"io"
"net/http"
)
func main() {
url := "https://api.up2data.ai/v1/batch"
body := bytes.NewBufferString(`{
"items": [
{
"type": "profile",
"url": "https://linkedin.com/in/a"
},
{
"type": "profile",
"url": "https://linkedin.com/in/b"
},
{
"type": "company",
"url": "https://linkedin.com/company/acme"
},
{
"operation": "posts.engagement.comments",
"body": {
"urn": "7267273010393358336",
"max_results": 500
}
}
],
"webhook_url": "https://app.example.com/hooks/up2data"
}`)
req, err := http.NewRequest("POST", url, body)
if err != nil {
panic(err)
}
req.Header.Set("X-API-Key", "YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
respBody, _ := io.ReadAll(resp.Body)
fmt.Println(string(respBody))
}#Response
{
"data": { "job_id": "job_7hd92kfa", "status": "processing", "total": 4, "estimated_completion": "2026-07-07T14:03:00Z" },
"meta": { "creditsUsed": 503, "creditsRemaining": 97916, "billed": true, "reason": "job_accepted", "requestId": "req_0pl2m8xz" }
}#Webhook payload
{
"event": "batch.completed",
"job": { "job_id": "job_7hd92kfa", "status": "completed", "total": 1000, "estimated_completion": "2026-07-07T14:03:00Z" }
}#Errors
| Status | Type | When | Billed |
|---|---|---|---|
| 400 | invalid_request | Malformed body, an item missing type/url or operation/body, an unknown or not-yet-enabled operation, page/per_page/pagination_token in an operation body, max_results on a scalar operation, a Sales Navigator item missing (or sending both of) filters/sales_nav_url, a search.jobs item with no criterion, or more than 1,000 items. | No |
| 401 | invalid_api_key | Missing, malformed, or revoked key. | No |
| 402 | insufficient_credits | Balance can't cover the credits this submission would charge. | No |
| 429 | rate_limited | Over the 300 requests/min limit. Honor Retry-After. | No |
| 500 | internal_error | Something broke on the server side. | No |
See the Errors guide for every error type this version can return.