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://docs.up2data.ai/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

Signals

Re-run a batch on a schedule: watch profiles, companies or posts and get a batch.completed webhook per run.

#What a signal is

A signal is a saved POST /v1/batch body plus a schedule. On every tick Up2Data submits that body as a batch job on your behalf, bills it at the normal batch rates from your balance at that moment, and sends a batch.completed webhook to the signal's webhook_url when the job finishes. Use it to watch a list of profiles or companies for changes, re-pull engagement on a set of posts every day, or run the same search every week.

Scheduling, reading, pausing, resuming and deleting a signal are all free. Only the runs cost credits, exactly what the same batch would cost if you submitted it yourself.

#Schedule a signal

bash
curl -X POST https://api.up2data.ai/v1/signals \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Watch key accounts",
    "schedule": { "interval_ms": 86400000, "start_at": "2026-11-01T08:00:00Z", "max_runs": 30 },
    "request": {
      "items": [
        { "type": "profile", "url": "https://linkedin.com/in/a" },
        { "type": "company", "url": "https://linkedin.com/company/acme" }
      ],
      "webhook_url": "https://your-server.com/hooks/up2data"
    }
  }'
json
{
  "data": {
    "signal_id": "9d1f4c2e-7b3a-4e8f-a1c5-2f6b8d9e0a11",
    "status": "active",
    "schedule": { "interval_ms": 86400000, "start_at": "2026-11-01T08:00:00Z", "ends_at": null, "max_runs": 30 },
    "run_count": 0,
    "next_run_at": "2026-11-01T08:00:00Z"
  },
  "meta": { "creditsUsed": 0, "creditsRemaining": 98413, "billed": false, "reason": "signal_created", "requestId": "req_t4sk0001" }
}

request is the exact batch body: every item shape, flag and operation that POST /v1/batch accepts works here. When you create the signal only items (1 to 1,000 objects) and webhook_url (https) are checked; the rest is validated when a run submits it.

#Pick a schedule

You want schedule
Every day, until you stop it { "interval_ms": 86400000 }
Tomorrow, the day after, and the day after that { "interval_ms": 86400000, "start_at": "<tomorrow 08:00>", "max_runs": 3 }
Every 6 hours until year end { "interval_ms": 21600000, "ends_at": "2026-12-31T00:00:00Z" }
Once, at a point in time { "interval_ms": 60000, "start_at": "<when>", "max_runs": 1 }

interval_ms is required and must be at least 60000. start_at defaults to now. Omit both ends_at and max_runs for an indefinite signal. A signal completes when run_count reaches max_runs or the next run would fall after ends_at; completed signals keep their history and can be revived by raising max_runs or moving ends_at with PATCH /v1/signals/{signal_id}.

After downtime on our side a missed tick runs once and the schedule realigns to now + interval_ms. There is no catch-up burst.

#Read the results

Each run creates one batch job. The signal carries the latest one as last_job_id, and GET /v1/signals/{signal_id}/runs lists every run with its job_id:

bash
curl https://api.up2data.ai/v1/signals/9d1f4c2e-7b3a-4e8f-a1c5-2f6b8d9e0a11/runs -H "X-API-Key: YOUR_API_KEY"

Then read a job like any other batch: GET /v1/batch/{job_id} for the summary and accumulated results, or GET /v1/batch/{job_id}/results for the incremental feed.

If you set request.webhook_url, every run's job sends batch.completed there. The payload names only the job_id, so either keep one webhook URL per signal or resolve the job through the runs list.

#When a run fails

Run outcome What happens to the signal
Job accepted (202) run_count increments, last_job_id updates, next tick scheduled
402 insufficient_credits Signal pauses with pause_reason insufficient_credits. The step is not consumed. Top up and resume; the same step runs on the next tick
400 invalid_request Signal pauses with pause_reason invalid_request, because the same body would fail every time. Fix request with PATCH and resume
429 rate_limited Run is deferred and retried after Retry-After under the same run_number. The step is not consumed
500, 504, network error Retried three times with backoff; then the run is failed, last_error is set, and the schedule moves on

Pausing a signal yourself with POST /v1/signals/{signal_id}/pause sets pause_reason to manual. A paused signal keeps its place: resuming it fires immediately if next_run_at is already past, otherwise at the next tick.

#Endpoints

Call Purpose
GET /v1/signals List signals, filter by status
POST /v1/signals Schedule a signal
GET /v1/signals/{signal_id} One signal
PATCH /v1/signals/{signal_id} Change name, schedule or request
DELETE /v1/signals/{signal_id} Delete a signal and its history
POST /v1/signals/{signal_id}/pause Pause
POST /v1/signals/{signal_id}/resume Resume
GET /v1/signals/{signal_id}/runs Run history with each run's job_id

All of them are free and count toward your per-key request limit like any other call. See Errors for the 409 conflict a completed signal returns on pause and resume.