# Changelog of LinkedIn Company 360 — Firmographics + Hiring + Ads Fused (`foxlabs/linkedin-company-360`) Actor

- **URL**: https://apify.com/foxlabs/linkedin-company-360/changelog.md
- **Full Actor documentation**: https://apify.com/foxlabs/linkedin-company-360.md

## Changelog

### 0.1.14 — 2026-09-10 — failed lookups become free (per-record billing) + README corrected

- **From 25 September 2026 you pay only for complete company records.** From that date the Actor charges a new `company-record` event for each complete company row instead of the per-dataset-item event, at the same price ($0.07). Every row that carries an `error` field — the company page could not be loaded, the URL was malformed, the company id was missing, or LinkedIn refused a requested signal — is pushed without a charge.
- **Timing:** Apify applies pricing changes after a 14-day notice. The code for the switch ships in this version and detects the new pricing by itself; until 25 September 2026 billing is exactly as before.
- **Fix — proxy sessions.** The session-id cleanup had lost a backslash (`[^w.~]` instead of `[^\w.~]`), so every request of a run shared one residential session (`_`). Each company now gets its own Apify Proxy session (`c_<slug>_<hash>_<n>`) for its page, job search, Ad Library searches and legal pages. When LinkedIn refuses a request (HTTP 403, 999, a 429 that survives one back-off, or an empty page), the session is retired and the request is retried from a fresh one — at most 3 sessions per company and 10 new sessions per run; every rotation is logged and the run's last log line counts them. A 403 from the company's own website is no longer retried.
- **Fix — job functions.** Title keywords now match word stems, so "Product Designer" counts as Design, "Product Marketing Manager" as Marketing, "Recruiter" as People & HR and "Data Scientist" as Data & AI. A function word right before "engineer…" or "architect…" decides the department: pre-sales titles ("Sales Engineer", "Sales Engineering", "Solutions Engineer / Architect", "Pre-sales Engineer") count as Sales, "Marketing Engineer" as Marketing, "Support Engineer" as Customer and "Data / ML / AI Engineer" as Data & AI; any other "…Engineer" is Engineering. On the same 400 real titles, `Other` fell from 89 to 57 (Stripe) and from 71 to 47 (Datadog), all 21 of Stripe's marketing titles now count as Marketing (before: 1), and Datadog's Sales count rose from 32 to 65.
- **Fix — a refused request is no longer a zero.** When LinkedIn refuses a job-search page, or the worldwide Ad Library search, after 4 attempts, that module's counts are `null` with `blocked: true`, `signalSummary` says "blocked by LinkedIn", the row carries `error` and is not charged. Before, refused pages were skipped, which reported fewer roles, 0 roles or "not-hiring". A refused country search only lowers a count that is already a lower bound: the count is kept and the new `advertising.searchesRefused` says how many searches failed. Every refused page is logged with its last HTTP status.
- **Fix — malformed URLs.** A company URL with a broken %-escape used to stop the whole run ("URI malformed"). It now becomes one row with `companyInput` and `error: "malformed company URL (broken %-escape)"`, is not charged, and the other companies still run.
- **Fix — rows without a company id.** When the LinkedIn company id cannot be read from the page, hiring and advertising cannot be queried: the row now carries `error`, is not charged, and its `note` says what was not collected. With hiring and advertising both switched off, such a row is complete and still charged.
- **Output changes:** new fields `companyInput`, `hiring.blocked`, `advertising.blocked` and `advertising.searchesRefused`; `error` now also marks incomplete rows; counts of a blocked module are `null` instead of numbers; `topDepartments` values shift as described above. No field was renamed or removed.
- **README corrected.** The 2026-09-07 README documented inputs this Actor does not have (`queries`, `maxResultsPerQuery`, `maxConcurrency`, `includeRaw`) and used a placeholder example with an undefined value. It also claimed name or registry-ID search (the Actor takes LinkedIn slugs or company URLs), a "registry" source with "formation / status monitoring" (the source is public LinkedIn pages), a `query` field on every row (there is none), "Primary industry (NACE)" (it is LinkedIn's industry label), status, legal form, formation date and registry contact fields (not in the output), troubleshooting built on name matching and `maxResultsPerQuery`, and "failed lookups are never billed" (they were billed as dataset items; they are free from 25 September 2026). The README now lists the real inputs, fields, pricing and limits.

### 0.1 — 2026-09-07

- **Enabled AI-agent payments (x402) + rebuilt the README to the full standard** (What-is / when, AI-agents + x402 agentic payments + MCP, Overview, Features, Use cases, Integration, FAQ, Troubleshooting, Support & contact).

### 0.0

- Initial release: data from public LinkedIn company pages, looked up by LinkedIn company slug or URL. *(Corrected 2026-09-10 — this line said "by name or registry ID".)*
