curl -X POST https://api.up2data.ai/v1/signals \
-H "X-API-Key: $UP2DATA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "CTO job change alert",
"tags": ["q3-pipeline"],
"external_id": "crm:workspace:42",
"metadata": { "tenant": "acme" },
"schedule": { "interval": "1d", "timezone": "America/New_York" },
"target": { "type": "profile", "linkedin_url": "https://linkedin.com/in/example" },
"criteria": { "mode": "field_change", "fields": ["headline", "position", "company"] },
"notify": {
"webhook_url": "https://app.example.com/hooks/up2data",
"webhook_secret": "whsec_k29fj20am"
}
}'
resp = requests.post(
"https://api.up2data.ai/v1/signals",
headers={"X-API-Key": UP2DATA_API_KEY},
json={
"name": "Headcount watch — Acme",
"tags": ["accounts"],
"external_id": "crm:workspace:42",
"metadata": {"tenant": "acme"},
"schedule": {"interval": "7d"},
"target": {"type": "company", "linkedin_url": "https://linkedin.com/company/acme"},
"criteria": {
"mode": "numeric_change",
"fields": [{"name": "headcount", "min_change_pct": 5}],
},
}, # notify omitted → account default webhook
)
signal = resp.json()["data"]
{
"data": {
"id": "sig_9dk2m1xq",
"name": "CTO job change alert",
"tags": ["q3-pipeline"],
"external_id": "crm:workspace:42",
"metadata": { "tenant": "acme" },
"enabled": true,
"status": "active",
"schedule": { "interval": "1d", "timezone": "America/New_York" },
"target": { "type": "profile", "linkedin_url": "https://linkedin.com/in/example" },
"criteria": { "mode": "field_change", "fields": ["headline", "position", "company"], "refire": "on_change_only" },
"notify": { "webhook_url": "https://app.example.com/hooks/up2data" },
"estimated_monthly_credits": 30,
"credits_used_this_month": 0,
"next_check_at": "2026-07-07T14:00:30Z",
"created_at": "2026-07-07T14:00:00Z"
},
"meta": { "creditsUsed": 0, "billed": false, "reason": "signal_created", "requestId": "req_sg81mm2p" }
}
Signals
Create a Signal
Define what to watch, how often to check, where to get pinged, and optional platform attribution.
POST
/
v1
/
signals
curl -X POST https://api.up2data.ai/v1/signals \
-H "X-API-Key: $UP2DATA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "CTO job change alert",
"tags": ["q3-pipeline"],
"external_id": "crm:workspace:42",
"metadata": { "tenant": "acme" },
"schedule": { "interval": "1d", "timezone": "America/New_York" },
"target": { "type": "profile", "linkedin_url": "https://linkedin.com/in/example" },
"criteria": { "mode": "field_change", "fields": ["headline", "position", "company"] },
"notify": {
"webhook_url": "https://app.example.com/hooks/up2data",
"webhook_secret": "whsec_k29fj20am"
}
}'
resp = requests.post(
"https://api.up2data.ai/v1/signals",
headers={"X-API-Key": UP2DATA_API_KEY},
json={
"name": "Headcount watch — Acme",
"tags": ["accounts"],
"external_id": "crm:workspace:42",
"metadata": {"tenant": "acme"},
"schedule": {"interval": "7d"},
"target": {"type": "company", "linkedin_url": "https://linkedin.com/company/acme"},
"criteria": {
"mode": "numeric_change",
"fields": [{"name": "headcount", "min_change_pct": 5}],
},
}, # notify omitted → account default webhook
)
signal = resp.json()["data"]
{
"data": {
"id": "sig_9dk2m1xq",
"name": "CTO job change alert",
"tags": ["q3-pipeline"],
"external_id": "crm:workspace:42",
"metadata": { "tenant": "acme" },
"enabled": true,
"status": "active",
"schedule": { "interval": "1d", "timezone": "America/New_York" },
"target": { "type": "profile", "linkedin_url": "https://linkedin.com/in/example" },
"criteria": { "mode": "field_change", "fields": ["headline", "position", "company"], "refire": "on_change_only" },
"notify": { "webhook_url": "https://app.example.com/hooks/up2data" },
"estimated_monthly_credits": 30,
"credits_used_this_month": 0,
"next_check_at": "2026-07-07T14:00:30Z",
"created_at": "2026-07-07T14:00:00Z"
},
"meta": { "creditsUsed": 0, "billed": false, "reason": "signal_created", "requestId": "req_sg81mm2p" }
}
Cost: Free to create · each scheduled check bills the underlying scrape (pay-per-check) · the first check establishes the baseline (billed, never triggers)
Creates a durable monitoring rule. Creating many Signals from one template? Use bulk create.
How Signals work
Signals are scheduled change monitoring, billed per check. See what you can watch and how it’s priced.
string
required
Human-readable label, shown in notifications and the dashboard.
string[]
Free-form labels for fleet management — filter lists and bulk pause/resume by tag.
string
Optional platform attribution key (1–256 characters). Filter lists and roll up usage with
GET /v1/usage/signals.object
Opaque JSON bag (max 8192 bytes serialized). Copied onto Triggers and webhook payloads for your tenant context.
boolean
default:"true"
Create paused by setting
false.object
required
object
required
Show Target fields
Show Target fields
string
required
profile | profile_list | company | company_jobs | post | post_search | people_search | company_search | post_reactions | post_comments | post_reposts.string
The entity to watch. Required for
profile, company, company_jobs, post, post_reactions, post_comments, post_reposts.string[]
For
profile_list: up to 1,000 profile URLs. Bills 1 credit per URL per check.object
For
company_jobs / people_search / company_search: same filters as the corresponding search endpoint. Mutually exclusive with sales_nav_url. For post_reactions: optional reaction_type (ALL | LIKE | EMPATHY | APPRECIATION | INTEREST | PRAISE | ENTERTAINMENT).string
For
people_search / company_search: paste a Sales Navigator search URL instead of structured filters. Mutually exclusive with filters.object
integer
default:"25"
For search-backed and post-engager targets: how many results to scrape and diff per check (1–100). Search-backed bill 1 credit per page of up to 25; engagers bill 1 credit per returned person.
object
required
Show Criteria fields
Show Criteria fields
string
required
field_change | text_match | new_entity | field_equals | numeric_change | expression.
Use new_entity on search-backed targets for Sales Nav–style net-new alerts.array
For
field_change / numeric_change: fields to watch, or ["*"] for any non-volatile field. Entries are strings or threshold objects: { "name": "headcount", "min_change_pct": 2 } (also min_change_abs). See Filtering.string
For
text_match / field_equals: the field to evaluate (e.g. text, headline).string
text_match: substring match (normalized).string[]
text_match: match if any listed term appears.string
text_match: RE2 pattern.string
field_equals: fire when the field equals this value.string
expression: a CEL expression over snapshot and diff, e.g. diff.headcount.after < diff.headcount.before * 0.9.boolean
default:"false"
For
new_entity only. Default false = additions only (Sales Nav net-new). Set true to also fire when entities drop out of the result set.string
default:"on_change_only"
on_change_only | always.object
Webhook destination — or omit entirely to use your account’s default webhook.
Response
object
The Signal, including
id, status, estimated_monthly_credits, credits_used_this_month, and next_check_at. The first check establishes the baseline snapshot — it is billed like any check, can never trigger, and appears in check logs with reason: "baseline_established".curl -X POST https://api.up2data.ai/v1/signals \
-H "X-API-Key: $UP2DATA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "CTO job change alert",
"tags": ["q3-pipeline"],
"external_id": "crm:workspace:42",
"metadata": { "tenant": "acme" },
"schedule": { "interval": "1d", "timezone": "America/New_York" },
"target": { "type": "profile", "linkedin_url": "https://linkedin.com/in/example" },
"criteria": { "mode": "field_change", "fields": ["headline", "position", "company"] },
"notify": {
"webhook_url": "https://app.example.com/hooks/up2data",
"webhook_secret": "whsec_k29fj20am"
}
}'
resp = requests.post(
"https://api.up2data.ai/v1/signals",
headers={"X-API-Key": UP2DATA_API_KEY},
json={
"name": "Headcount watch — Acme",
"tags": ["accounts"],
"external_id": "crm:workspace:42",
"metadata": {"tenant": "acme"},
"schedule": {"interval": "7d"},
"target": {"type": "company", "linkedin_url": "https://linkedin.com/company/acme"},
"criteria": {
"mode": "numeric_change",
"fields": [{"name": "headcount", "min_change_pct": 5}],
},
}, # notify omitted → account default webhook
)
signal = resp.json()["data"]
{
"data": {
"id": "sig_9dk2m1xq",
"name": "CTO job change alert",
"tags": ["q3-pipeline"],
"external_id": "crm:workspace:42",
"metadata": { "tenant": "acme" },
"enabled": true,
"status": "active",
"schedule": { "interval": "1d", "timezone": "America/New_York" },
"target": { "type": "profile", "linkedin_url": "https://linkedin.com/in/example" },
"criteria": { "mode": "field_change", "fields": ["headline", "position", "company"], "refire": "on_change_only" },
"notify": { "webhook_url": "https://app.example.com/hooks/up2data" },
"estimated_monthly_credits": 30,
"credits_used_this_month": 0,
"next_check_at": "2026-07-07T14:00:30Z",
"created_at": "2026-07-07T14:00:00Z"
},
"meta": { "creditsUsed": 0, "billed": false, "reason": "signal_created", "requestId": "req_sg81mm2p" }
}
Signals pause — never silently skip — on insufficient balance, a blown monitoring budget, or a dead webhook URL. Each pause fires a
signal.paused event with the reason.