TikTok Jobs API
Query TikTok's postings through one API with ?source=tiktok — plus the website-path header its API 400s without.
TikTok is not a platform. It's one employer with a bespoke careers site at
lifeattiktok.com, and this adapter reaches TikTok's postings and nothing else.
One Workday adapter reaches every Workday tenant; this one reaches
one company.
It's here because TikTok is large enough for its jobs to matter on their own, and because a bespoke site has no vendor fingerprint for platform discovery to find. The price is a dedicated adapter, owned forever, breaking on TikTok's schedule.
How TikTok's careers API actually works#
A POST, not bot-protected, descriptions and structured salary inline:
curl -X POST "https://api.lifeattiktok.com/api/v1/public/supplier/search/job/posts" \
-H "website-path: tiktok" \
-H "origin: https://lifeattiktok.com" \
-H "referer: https://lifeattiktok.com/" \
-d '{"keyword":"","limit":100,"offset":0}'Header-gated, not bot-protected
There's no Akamai here, no browser needed, no TLS impersonation — plain HTTP
works. But the request is header-gated: website-path: tiktok plus a
matching origin and referer are required, and without them the API returns
400.
That's the good kind of quirk, at least: it fails loudly. Contrast Apple, where a missing block returns zero results and a clean 200. A 400 tells you something is wrong; a silent zero doesn't.
website-path exists because the same backend serves several ByteDance career
sites. You're telling it which brand you're asking about.
What the adapter reads#
Everything comes back in one page, no detail call:
| Field | What it becomes |
|---|---|
title, description, requirement | Body assembled as description + a Requirements section |
code | The requisition id ("A220892") |
job_category.en_name | Department |
job_subject.en_name | Team |
recruit_type.en_name | "Regular", "Intern" — employment type and commitment |
job_post_info | { min_salary, max_salary, currency } — structured pay |
city_info | A nested location chain (see below) |
publish_time | Epoch timestamp |
The description is two fields, not one: description plus requirement,
reassembled with a heading. Read description alone and you get the pitch without
the requirements — the same split Lever and Amazon
have, in a third arrangement. en_name throughout is the tell that this is a
bilingual system with English alongside Chinese.
Locations are a linked list#
city_info isn't a string or a flat object. It's a parent chain: a city node
whose parent is a region whose parent is a country. Getting a location means
walking the chain to the root and joining what you find.
Read city_info.en_name and stop, and every location loses its country — leaving
you a bare city to geocode, which is how Cambridge becomes the wrong one.
What bites you#
publish_time is an epoch with no declared unit. Sometimes seconds,
sometimes milliseconds. Treat seconds as milliseconds and the posting lands in
1970; treat milliseconds as seconds and it lands tens of thousands of years from
now. The adapter switches on magnitude — under a trillion means seconds. There's
nothing in the payload that tells you which you're holding.
The salary has no period
job_post_info gives a min, a max and a currency. It does not say whether
that range is annual, monthly or hourly.
The numbers are real and their unit isn't in the data. A range that's monthly in one market and annual in another looks identical in the payload, and annualising the wrong one is off by a factor of twelve. The adapter stores the unit as null rather than guessing, because a wrong unit is worse than a missing one.
One adapter, one company, forever. Same deal as Apple, Amazon, Uber, Google, Microsoft and IBM: a dedicated adapter maintained indefinitely, breaking independently, for one employer's jobs.
Or use ours#
Three headers, offset pagination, a two-part body reassembled, a parent chain
walked to a country, an epoch disambiguated by size, and a salary whose period
isn't stated left honestly unitless — with TikTok's rows in the same
Job shape as every other source,
dedup against the same role on LinkedIn, expiry by board diffing
since the payload declares none, and a single-company adapter's upkeep being ours
rather than yours.
Query it with ?source=tiktok — the same string that keys
/v1/meta → sources. That endpoint is the live
vocabulary and carries the counts as they move; it's worth reading rather than
trusting a list on a page.
Every source above normalises onto the same schema, with the hiring company already joined onto each posting.
Get an API key