BetterJobs has no native Make app. You use three built-in modules:
- HTTP → Make a request to call the API.
- Webhooks → Custom webhook to receive events.
- Tools and a filter to check the event signature.
Search jobs with the HTTP module
Section titled “Search jobs with the HTTP module”-
Add HTTP → Make a request.
-
Set:
Setting Value URL https://api.betterjobs.cc/v1/jobs/searchMethod POSTHeader AuthorizationBearer bj_live_...Header BetterJobs-Version2026-10-01Body type Raw Content type JSON ( application/json)Parse response Yes -
Paste the request content:
{"filters": {"title_or": ["Head of RevOps", "Head of Revenue Operations"],"country_code_or": ["DE", "AT", "CH"],"posted_within_days": 7},"waterfall": {"strategy": "cheapest_first","max_credits": 100},"limit": 25}To use a value from an earlier module, place the cursor inside the quotes and pick the item from the mapping panel, for example a company domain inside
company_domain_or. -
Add an Iterator after it and map
data. Each bundle is now one canonical job.
Map fields such as title, company.domain, apply_url and id into the next module. Store id. Fetching the same job later is free. All fields are in the field dictionary.
Errors and rate limits
Section titled “Errors and rate limits”- Add a Break error handler to the HTTP module for
429and500. It retries the bundle later. A500is safe to retry with the sameIdempotency-Keyheader: you are never charged twice. See Idempotency. 200withmetadata.status=partialis a success. One provider failed or timed out, and you pay only for the jobs returned. See Provider status.- A misspelled filter returns
400 unknown_filter. Readerror.paramin the module output. See Errors.
Large searches: async and polling
Section titled “Large searches: async and polling”One request returns at most 100 jobs. For backfills up to 10,000 jobs, use an async search. Make scenarios should not wait in a loop, so split the work in two scenarios.
Scenario A: start the search.
- HTTP → Make a request:
POST https://api.betterjobs.cc/v1/searcheswith the same headers. The body takesfilters,waterfallandlimit(up to 10,000). Add anIdempotency-Keyheader so a retried run does not start a second search. - Save the returned
id(srch_...) in a Data store record with adoneflag set to false.
Scenario B: poll and collect. Schedule it every few minutes.
- Data store → Search records where
doneis false. - HTTP → Make a request:
GET https://api.betterjobs.cc/v1/searches/{id}with the stored id. - Add a Router on
status:queuedorrunning: do nothing. The next run checks again.on_hold: you ran out of credits. The search resumes after a top-up.completedorpartial: read results, then setdoneto true.failed: setdoneto true and alert someone.
- To read results, page with
GET /v1/searches/{id}?limit=100&cursor=.... Use a Repeater with Repeats set toceil(jobs_found / 100). Keepnext_cursorin a Set variable with lifetime One execution, and pass it ascursoron the next repeat.
Branch on status, never on the HTTP code. GET /v1/searches/{id} returns 200 for every state. Reading results is free: jobs are charged when the search collects them. See Async searches.
Receive webhooks
Section titled “Receive webhooks”BetterJobs POSTs one event per request to your endpoint and signs it with the BetterJobs-Signature header:
BetterJobs-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">You compute the same HMAC with your endpoint secret and compare it to v1.
1. Create the custom webhook
Section titled “1. Create the custom webhook”- Add Webhooks → Custom webhook as the first module and create a new hook.
- Open Advanced settings. Turn on Get request headers and JSON pass-through. Pass-through keeps the raw body as one text value. The signature covers those exact bytes.
- Copy the webhook URL. Use it as
webhook_urlinPOST /v1/watchesorPOST /v1/searches. - Click Redetermine data structure, then send a test event, for example with
POST /v1/webhooks/replayon an existing event id.
Make responds 200 as soon as the webhook accepts the request, which meets the 10-second deadline.
2. Check the signature
Section titled “2. Check the signature”Add Tools → Set multiple variables after the webhook:
| Variable | Value |
|---|---|
sig |
The BetterJobs-Signature value from the headers array. Use map() on the headers with key value, filtered by name, then first(). Check the exact header name in the webhook output. |
t |
sig split on ,, first part, with t= removed |
v1 |
sig split on ,, last part, with v1= removed |
expected |
sha256() of the text t + . + the raw body value, encoding hex, key = your endpoint secret |
Then set a filter on the link to the next module: expected Equal to v1. Bundles that fail the filter stop there.
3. Parse and route
Section titled “3. Parse and route”Add JSON → Parse JSON on the raw body, then a Router on type:
job.opened: start outbound. It is the only event that should.job.reposted: update your copy. Never re-trigger outbound.job.closed: stop sequences for that job.search.completed: fetch results withGET /v1/searches/{id}.
What each event means and costs is in the event catalog.
Failed deliveries
Section titled “Failed deliveries”If a delivery does not get a 2xx within 10 seconds, BetterJobs retries for 24 hours with backoff. After that, or after you fix a broken scenario, replay any event with POST /v1/webhooks/replay. Replays are free. See Webhooks.
Related
Section titled “Related”- Detect hiring changes for watches end to end.
- Pagination for how cursors work.
- Troubleshooting for signature mismatches and
402.