Hyreflow Native
First-party scrapers metered in Hyreflow credits with no separate subscription: job scrapes across career pages, LinkedIn, Indeed and Arbeitsagentur (launch, then poll — charged per job returned), and LinkedIn post feeds for a person or a company (one call — charged per request).
Reach for Hyreflow Native on two triggers. "Find the open roles" — pull everything a company is hiring for off its career site, or search LinkedIn / Indeed / the German federal board by title and location. "What have they been posting?" — read a person's or a company's recent LinkedIn posts for launch, hiring, funding, exec-commentary and layoff signals. First-party scrapers you run on Hyreflow credits: no scraper subscription, no API key to manage.
Native (Hyreflow-metered, no BYOK) — first-party capability billed in Hyreflow credits; the provider-precedence waterfall does not apply.
Job scrapes and post feeds work differently, and they bill differently:
| Shape | Billing | |
|---|---|---|
| Job scrapes | Asynchronous — launch, then poll | Per job delivered; the launch is free, and an empty result costs nothing |
| Post feeds | A single call returns a page of posts | Per request — one page of up to 50 posts, whatever the page carries |
Job scrapes: launch, then poll
A scrape takes a few minutes, so every job scraper is a two-step flow:
- Launch with a
scrape_*tool (or its short alias) → returns{ request_id, status: "QUEUED" }. Free. - Poll the matching
get_*tool with thatrequest_id→statusmovesQUEUED→RUNNING→COMPLETED. WhenCOMPLETED,resultcarries a page ofjobsplusjobs_total,jobs_returned,jobs_filtered_out,limit,offset,has_more, andwarnings. Poll roughly every 10s: re-polls dedup per job, so a tighter loop never costs extra — it only shortens the wait.
You're charged once per unique job delivered — each job in a scrape is billed exactly once. Re-polling, widening the limit, or overlapping page windows never double-charge; paging forward bills only the newly-delivered jobs.
Because a launch is free and returns immediately, you don't have to run scrapes one at a time. Launch several scrape_* calls up front, collect their request_ids, and poll them together — launches aren't serialized, and a batch of around 20 runs is a practical working size. A multi-angle sweep (the same titles across career pages, LinkedIn, Indeed, and Arbeitsagentur, or the same query across several cities) then costs roughly one scrape's wall-clock instead of the sum of every scrape run sequentially. Each run in the batch bills its own delivered jobs as you poll it, so a wide batch commits that spend at once — size the batch accordingly.
jobs_returned is only the current page — with the default limit of 10, a first poll showing 10 jobs says nothing about how many roles the scrape actually found. Check jobs_total and has_more to judge coverage before deciding you need another scrape.
Job scrapers
| Launch (free) | Alias | Poll for results | Scrapes |
|---|---|---|---|
scrape_career_pages | career_pages | get_career_pages | A company's career site + its ATS (Greenhouse, Lever, Workday, Ashby) |
scrape_linkedin_jobs | linkedin_jobs | get_linkedin_jobs | LinkedIn jobs by title search and/or a company's LinkedIn page |
scrape_indeed_jobs | indeed_jobs | get_indeed_jobs | Indeed jobs by title + location (regional hosts) |
scrape_arbeitsagentur_jobs | arbeitsagentur_jobs | get_arbeitsagentur_jobs | Bundesagentur für Arbeit (German federal job board) |
The get_* pollers accept limit/offset (default 10, max 50) to page through result.jobs. A limit above 50 is rejected rather than quietly trimmed, so a paging loop can't silently skip the jobs a shortened page would have left behind. Advancing to a new page bills the jobs that page delivers. Dedup makes re-polling a window you already fetched free — it does not make the next window free, so has_more: true is not a reason to keep going. Stop once you have what was asked for; to walk the whole result deliberately, keep incrementing offset by limit and re-polling while result.has_more is true.
Post feeds: one call, one page
A post feed is not asynchronous — one call returns the posts inline, newest first:
| Tool | Alias | Reads |
|---|---|---|
get_profile_posts | profile_posts | A person's recent posts, from the handle in linkedin.com/in/<username> |
get_company_posts | company_posts | A company page's recent posts, from the handle in linkedin.com/company/<username> |
One request returns one page of up to 50 posts and is charged as one request. To walk further back, pass the next_start the previous call returned while has_more is true — each page is another request, so it is another charge. limit trims how many posts come back (handy when you only want the last handful in context); it does not change what a request costs, so there is no saving in asking for fewer — and it is not the paging step, which is why next_start is the value to advance on.
Every post carries its full text, the post URL, when it was posted (both a timestamp and a date), reaction / comment / repost counts, the content type, the author, and any companies tagged in it. A company feed also reports total and total_page for the whole feed.
A request is charged whether or not the handle resolves — a page of 50 posts and an unreachable handle cost the same. When a handle can't be read, success is false and message says why, so check the handle against the profile URL before spending a request on it.
# a person's recent posts — one call, one charge
hyreflow tools execute profile_posts --payload '{"username":"acme-dana-reed","limit":10}'
# a company page's feed, second page
hyreflow tools execute company_posts --payload '{"username":"acme","start":50}'Guidance
- Career pages: narrow with
target_titles(a list) ortarget_titles_prompt(a plain-English filter) — use one, not both. - LinkedIn / Indeed: pass
titles_query(a string or an OR-list of titles) andlocations;countryselects the regional site.company_urlon LinkedIn pulls a whole company's jobs. rowsandlimitare separate knobs — size both to what was asked.rowson the launch bounds what the scraper collects upstream (Indeed defaults to 25; LinkedIn to the upstream default);limiton the poll bounds what one page hands back (10). They are independent, sorows: 15with a default poll returns 10. For 15 roles sendrows: 15and poll withlimit: 15— one poll then delivers the ask and bills exactly it.arbeitsagentur_jobsandcareer_pagestake norows, so there the poll'slimitand stopping at the ask are the only guard.- Arbeitsagentur:
titleand/orlocation;radiusis in kilometres. - Each returned job carries title, location, employment type, salary (when available), the apply URL, and company fields — feed them straight into a sourcing or BD play.
- Post feeds:
usernameis the handle out of the LinkedIn URL, not the display name and not the full URL.
# 1. launch (free)
hyreflow tools execute linkedin_jobs \
--payload '{"titles_query":"recruiter","locations":["London"],"rows":15}'
# → { "request_id": "...", "status": "QUEUED" }
# 2. poll until COMPLETED (charged per job in result.jobs)
hyreflow tools execute hyreflow_native_get_linkedin_jobs \
--payload '{"request_id":"<id>","limit":15}'A job scrape is asynchronous — the launch returns a handle, not jobs. Keep polling the matching get_* until status is COMPLETED (or FAILED); a still-running or empty result costs nothing. A post feed has no launch half: the single call returns the posts.