Install the Up2Data skill

Teach Claude Code to integrate the Up2Data API. Installs once, then loads automatically whenever you ask it about Up2Data.

  1. Run this in your terminal
    bash
    mkdir -p ~/.claude/skills/up2data-api && curl -fsSL https://up2data.ai/docs/v1/skill.md -o ~/.claude/skills/up2data-api/SKILL.md
  2. Restart Claude CodeSo it picks up the new skill.
  3. Try it"Enrich these LinkedIn profiles with Up2Data and send results to my webhook"

Requires Claude Code. macOS, Linux, or WSL.

Prefer to read it first? View the skill file

Create a batch job

Submit up to 1,000 mixed enrich and operation items as one asynchronous job.

POSThttps://api.up2data.ai/v1/batch
Billed at submissionprofile, company, and post items: 1 credit each, never refunded. Every other operation reserves the worst case at submission and settles to what it actually billed once its queue finishes - see the operations table belowasynchronous

Every /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
Warning

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.

Info

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

FieldTypeNotes
itemsrequiredobject[]Up to 1,000 items, freely mixed.
items[].typestringEnrich item. Enum: profile, company, post. Required with url.
items[].urlstringRequired with type.
items[].operationstringOperation 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[].bodyobjectRequest 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_urlstringHTTPS endpoint for the batch.completed event. Omit for polling-only.
Idempotency-Keystring (header)Optional. A retried submission with the same key returns the original job; no duplicate is created.

#Call it

cURL
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"
}'

#Response

202
{
  "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

batch.completed
{
  "event": "batch.completed",
  "job": { "job_id": "job_7hd92kfa", "status": "completed", "total": 1000, "estimated_completion": "2026-07-07T14:03:00Z" }
}

#Errors

StatusTypeWhenBilled
400invalid_requestMalformed 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
401invalid_api_keyMissing, malformed, or revoked key.No
402insufficient_creditsBalance can't cover the credits this submission would charge.No
429rate_limitedOver the 300 requests/min limit. Honor Retry-After.No
500internal_errorSomething broke on the server side.No

See the Errors guide for every error type this version can return.