Watches
Choose which jobs a job.closed webhook tells you about, and for how long. Jobs returned by search are watched for 90 days automatically; add others by id for 1 to 365 days with /v1/watches. Free, and a free status check.
A watch is a job your account wants to hear about when it closes. The
job.closed webhook fires only for watched jobs, so instead
of re-fetching the jobs you hold by id to find out which ones closed, you are told.
There are two ways a job becomes watched:
| Origin | How | Expires |
|---|---|---|
search | Automatic: every job a search returns to your account. | 90 days after the search last returned it. |
manual | POST /v1/watches with its id. | After watch_days (1 to 365), 90 days if you don't say. |
A job watched both ways is manual. Watching a job again with POST /v1/watches sets its expiry to
the watch_days you send, whether that is sooner or later than before. A search that returns a
watched job only ever pushes its expiry later: it never cuts short a period you chose. Every watch
also ends when its job closes, or when you delete it. Watches are available on paid plans (the same plans as
webhooks) and cost no credits. Limits: 100,000 watches on Builder and Growth, 1,000,000 on Scale and
Business.
All three endpoints accept the same credentials as every other /v1 endpoint: an API key
(Authorization: Bearer jp_live_... or x-api-key) or an OAuth access token.
Watch jobs
POST https://api.jobspipe.dev/v1/watchescurl https://api.jobspipe.dev/v1/watches \
-H "Authorization: Bearer $JOBSPIPE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"job_ids": ["4464825573", "8739185002", "no-such-job"], "watch_days": 30}'| Field | Required | Description |
|---|---|---|
job_ids | yes | Up to 1,000 ids, as search returned them. |
watch_days | no | How many days to watch them, a whole number from 1 to 365. Default 90. |
Each id is looked up, so the response doubles as a free "is it still open?" check:
{
"watched": [
{ "job_id": "4464825573", "source_provider": "greenhouse", "status": "active" }
],
"already_closed": [
{ "job_id": "8739185002", "closed_at": "2026-09-20T00:00:00Z", "closed_reason": "gone" }
],
"not_found": ["no-such-job"],
"expires_at": "2026-10-24T10:00:00.000Z",
"watch_count": 1,
"max_watches": 100000
}| Field | Description |
|---|---|
watched | Open jobs, now watched. Watching one again is harmless and resets its period. |
already_closed | Jobs that are already closed. They are not watched: there is nothing left to wait for. |
not_found | Ids that match no job. Not watched. |
expires_at | When the jobs in watched stop being watched, unless they close first. |
watch_count | Watches on the account after this request. |
max_watches | The plan's limit. |
A request that would take the account past its limit is refused with 402 and adds nothing.
Ids you already watch do not count toward it.
Stop watching
DELETE https://api.jobspipe.dev/v1/watchescurl -X DELETE https://api.jobspipe.dev/v1/watches \
-H "Authorization: Bearer $JOBSPIPE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"job_ids": ["4464825573"]}'{ "removed": 1 }Ids you were not watching are ignored.
List watches
GET https://api.jobspipe.dev/v1/watches?limit=100&cursor=...Pages through your watches in job id order. limit is 1 to 1,000 (default 100). Pass
next_cursor back as cursor until it is null.
{
"data": [
{
"job_id": "4464825573",
"source_provider": "greenhouse",
"origin": "search",
"created_at": "2026-09-24T10:00:00.000Z",
"expires_at": "2026-12-23T10:00:00.000Z"
}
],
"next_cursor": null,
"watch_count": 1,
"max_watches": 100000
}Errors
| Status | When |
|---|---|
400 | job_ids missing, empty or over 1,000; watch_days not a whole number from 1 to 365; limit out of range. |
401 | No valid credential. |
402 | The account is on the free plan, or the request would pass max_watches. |