Vultboard

Developers

Jobs API

Send your open roles from your own systems, in one call or one at a time. Free for verified companies.

Before you start

If your roles are on Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Recruitee, Personio, Teamtailor or Workday, you don't need this: we read those boards automatically. The API is for everything else, or for roles that aren't on your public board. Prefer a spreadsheet? Upload a CSV from your employer dashboard instead.

Claim your company, then create a key under Bulk upload and API. Send it with every request:

Authorization: Bearer vb_live_…

Replace all your roles

One call with every open role. With "mode": "full_sync", roles you pushed before that aren't in the batch are closed. A full sync only runs when every job is valid, so a typo can't close your listings.

curl -X POST https://vultboard.com/api/v1/jobs \
  -H "Authorization: Bearer vb_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "full_sync",
    "jobs": [{
      "externalId": "ml-eng-1",
      "title": "Machine Learning Engineer",
      "description": "## What you'll do\n\nTrain and ship models.",
      "applyUrl": "https://example.com/careers/ml-eng-1",
      "workplace": "hybrid",
      "locations": ["London", "Berlin"],
      "salary": { "min": 120000, "max": 160000, "currency": "GBP", "period": "year" }
    }]
  }'

The default mode, upsert, adds and updates without closing anything; valid jobs are applied and invalid ones are reported. Up to 5,000 jobs per batch.

{
  "created": 1, "updated": 0, "unchanged": 0, "closed": 2,
  "skipped": [],
  "errors": [{ "index": 3, "externalId": "x", "errors": ["applyUrl: Use an https:// link"] }]
}

One role at a time

  • PUT /api/v1/jobs/:externalId creates or updates one role (the same fields, without externalId).
  • DELETE /api/v1/jobs/:externalId closes it now.
  • GET /api/v1/jobs lists your pushed roles with their public URL, status and when each expires.

Fields

  • externalId (required): your id for the role. Sending it again updates it.
  • title, description, applyUrl (https) and workplace ("remote", "hybrid", "onsite") are required.
  • description is Markdown, or set descriptionFormat to "html" to send HTML from your ATS. Everything is sanitised.
  • locations: cities, countries or "Remote (Europe)".
  • employmentType: "full_time", "part_time", "contract", "internship". Defaults to full-time.
  • salary: min, max, a three-letter currency and a period ("hour", "day", "month", "year").
  • department and publishedAt (ISO date, not in the future) are optional.

How pushed roles are listed

  • Each role stays listed for 30 days after it was last sent. Send it again, or run a daily full sync, to keep it live.
  • Roles are categorised like every other listing. At companies outside AI and web3, only AI and web3 roles are listed; others come back under skipped.
  • Errors look like { "error": { "code", "message" } }: 401 for a missing or revoked key, 413 for bodies over 20 MB, 422 for invalid jobs, 429 above 120 requests a minute per key (see Retry-After).