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

Batch jobs

Submit up to 1,000 mixed items in one call and read results by webhook or by polling.

#When to reach for batch

The sync endpoints - profiles/enrich, companies/enrich, posts/enrich - are fine up to a few hundred records. Past that, use POST /v1/batch: submit up to 1,000 items in one job, processed in parallel, with results delivered by webhook or by polling.

Every /v1 capability is batchable, as one of two item shapes. Three enrich shapes:

type Shape
profile { "type": "profile", "url": "..." }
company { "type": "company", "url": "..." }
post { "type": "post", "url": "..." }

or an operation item, running any other /v1 capability as a batch item instead of a loop of sync calls:

json
{ "operation": "posts.engagement.comments", "body": { "urn": "7267273010393358336", "max_results": 500 } }

operation is one of 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, or search.posts. body is the same shape as that operation's own sync request body, except page, per_page, and pagination_token are never accepted - batch paginates through everything up to max_results on its own. max_results defaults to 100 for every non-scalar operation, with its own cap per operation (2,500 for people search, 1,000 for company search, 500 or 1,000 for everything else); a scalar operation (companies.jobs_count, companies.headcount, jobs.enrich) rejects max_results entirely. search.people.sales_nav and search.companies.sales_nav take filters or sales_nav_url, exactly one, same as their sync endpoints. See POST /v1/batch for the full table of every operation's max_results default and cap, billing, and result row type.

#Submit a job

bash
curl -X POST https://api.up2data.ai/v1/batch \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: import-chunk-014" \
  -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" }
    ],
    "webhook_url": "https://your-server.com/hooks/up2data"
  }'
json
{
  "data": { "job_id": "job_7hd92kfa", "status": "processing", "total": 3, "estimated_completion": "2026-07-07T14:03:00Z" },
  "meta": { "creditsUsed": 3, "creditsRemaining": 98416, "billed": true, "reason": "job_accepted", "requestId": "req_0pl2m8xz" }
}

Submission is a single request no matter how many items you send, and it's billed for the full total right then - see Credits for the per-item rates and Rate limits for the request limits.

Info

Send an Idempotency-Key header with your submission. Retrying it with the same key returns the original job instead of creating a duplicate or charging twice - useful when a submission times out and you're not sure it landed.

#Get results

Poll the job directly:

bash
curl https://api.up2data.ai/v1/batch/job_7hd92kfa -H "X-API-Key: YOUR_API_KEY"
json
{
  "data": {
    "job_id": "job_7hd92kfa", "status": "completed", "total": 1000, "succeeded": 968, "failed": 32,
    "results": [
      { "type": "profile", "operation": "enrich.profile", "url": "https://linkedin.com/in/a", "status": "ok", "input": "https://linkedin.com/in/a", "data": { "full_name": "...", "headline": "..." } },
      { "type": "company", "operation": "enrich.company", "url": "https://linkedin.com/company/acme", "status": "ok", "input": "https://linkedin.com/company/acme", "data": { "name": "Acme", "headcount": 512 } }
    ],
    "failures": [
      { "type": "profile", "url": "https://linkedin.com/in/gone", "status": "unprocessable_target" },
      { "type": "company", "url": "https://linkedin.com/company/timed-out", "status": "upstream_timeout" }
    ]
  },
  "meta": { "creditsUsed": 1000, "creditsRemaining": 421012, "billed": true, "reason": "job_completed", "requestId": "req_v82n1kd0" }
}

Every results and failures entry carries the operation that produced it, and every results entry also carries input, echoing what you submitted for it.

All 1,000 items here are profile and company enrich items, charged when the job was submitted, and none of the 32 failures refund anything: a results entry, an unprocessable_target failure, and a 500 or 504 failure all bill the same - 1 credit for a profile, company, or post, or a max_results-sized reservation for a Sales Navigator search item. The job's creditsUsed still shows the full 1,000 - see Credits. Every other operation item refunds differently: it reserves the same worst case at submission, but settles down to what it actually billed once its queue finishes, so a failed or empty item on those operations costs 0.

GET /v1/batch/{job_id} is free to poll as often as you like, whether the job is still running or done. Or set webhook_url and listen for batch.completed instead - see Webhooks.

For large jobs, GET /v1/batch/{job_id}/results returns a cursor page of result entries instead of the whole job at once, so you can stream results in while the job is still processing instead of re-fetching a growing results array on every poll:

bash
curl "https://api.up2data.ai/v1/batch/job_7hd92kfa/results?limit=100" -H "X-API-Key: YOUR_API_KEY"

Pass the next_cursor from each response back in as cursor until has_more is false.

#Credits

Submitting a job reserves the worst case for every item at once: 1 credit per profile, company, or post item, and each operation item at its own rate - see the full operations table for exact numbers. A profile, company, post, or Sales Navigator search item never refunds for a failed item - a 422 unprocessable_target item and a retryable 500 or 504 failure both bill the same as a success; only a Sales Navigator search item's unused max_results reservation refunds, once that search runs and comes back short. Every other operation settles down to what it actually billed once its queue finishes: a 422 unprocessable_target failure, a company or job that isn't found, or an empty page all cost 0 and refund in full. See Credits for the full table.