> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fetchin.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Apify Compatibility

> Drop-in replacement for Apify actors — keep your apify-client code, just change the base URL

## Overview

Fetchin exposes an **Apify-compatible endpoint**. If you already run B2B data
actors through Apify, you can keep your existing code and the official Apify
client libraries — you only change the **base URL** to Fetchin and use your
Fetchin API key as the token.

Everything runs **asynchronously**, exactly like Apify: you start an actor run,
we queue it, and you poll for the result and read the dataset. Batches are
buffered and drained at your plan's RPS.

<Card title="Base URL" icon="link">
  `https://api.fetchin.io/v1/apify`
</Card>

Point any Apify client at that base URL and pass your Fetchin API key as the
token. That's the whole change.

## Supported actors

The following Apify actors are supported today. Reference an actor by its raw
**actor ID** or by the `username/actor-name` form your existing integration
already uses — both resolve.

| Apify actor                                                                                          | Actor ID            | What it returns                                                                                                 |
| ---------------------------------------------------------------------------------------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------- |
| [`supreme_coder/linkedin-profile-scraper`](https://apify.com/supreme_coder/linkedin-profile-scraper) | `yZnhB5JewWf9xSmoM` | A full professional profile (one item per profile). Input `scrapeCompany: true` adds the current-company block. |
| [`supreme_coder/linkedin-post`](https://apify.com/supreme_coder/linkedin-post)                       | `Wpp1BZ6yGWjySadk3` | A profile's posts. Input `deepScrape: true` adds reactions + comments per post.                                 |
| [`apimaestro/linkedin-profile-comments`](https://apify.com/apimaestro/linkedin-profile-comments)     | `7TNcROe1C2CQDO3wl` | Comments made by a profile (paginated).                                                                         |
| [`apimaestro/linkedin-profile-reactions`](https://apify.com/apimaestro/linkedin-profile-reactions)   | `FNhKFjeL8hWQtMeZI` | Reactions made by a profile (paginated).                                                                        |

We map the output fields to match each actor as closely as possible, so your
downstream code keeps working unchanged.

<Info>
  **Need another actor?** If you rely on an actor that isn't in this list,
  reach out via the chat on [fetchin.io](https://fetchin.io) or email us — we
  add actors on request.
</Info>

## Usage

### JavaScript / Node (`apify-client`)

```js theme={null}
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({
  token: process.env.FETCHIN_API_KEY,
  baseUrl: 'https://api.fetchin.io/v1/apify', // the only change
});

// Start a run and wait for it to finish
const run = await client
  .actor('yZnhB5JewWf9xSmoM')
  .call({ urls: [{ url: 'https://www.linkedin.com/in/williamhgates/' }], scrapeCompany: true });

// Read the dataset
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

### Python (`apify-client`)

```python theme={null}
from apify_client import ApifyClient
import os

client = ApifyClient(
    token=os.environ["FETCHIN_API_KEY"],
    api_url="https://api.fetchin.io/v1/apify",  # the only change
)

run = client.actor("yZnhB5JewWf9xSmoM").call(
    run_input={"urls": [{"url": "https://www.linkedin.com/in/williamhgates/"}], "scrapeCompany": True}
)

items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)
```

### HTTP (any language)

```bash theme={null}
# 1. Start a run
curl -s -X POST \
  'https://api.fetchin.io/v1/apify/v2/acts/yZnhB5JewWf9xSmoM/runs' \
  -H 'Authorization: Bearer '"$FETCHIN_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"urls":[{"url":"https://www.linkedin.com/in/williamhgates/"}],"scrapeCompany":true}'
# -> { "data": { "id": "<runId>", "defaultDatasetId": "<datasetId>", "status": "READY", ... } }

# 2. Poll the run until status is SUCCEEDED (waitForFinish long-polls up to 60s)
curl -s \
  'https://api.fetchin.io/v1/apify/v2/actor-runs/<runId>?waitForFinish=60' \
  -H 'Authorization: Bearer '"$FETCHIN_API_KEY"

# 3. Read the dataset (a JSON array of items)
curl -s \
  'https://api.fetchin.io/v1/apify/v2/datasets/<datasetId>/items' \
  -H 'Authorization: Bearer '"$FETCHIN_API_KEY"
```

## Pagination (comments & reactions)

The paginated actors return **one page per run**. To page, re-run the actor
with the `pagination_token` from the previous page's items and an incremented
`page_number` — exactly as with Apify:

```js theme={null}
let pageNumber = 1;
let token = null;
do {
  const run = await client
    .actor('FNhKFjeL8hWQtMeZI')
    .call({ username: 'williamhgates', page_number: pageNumber, pagination_token: token });
  const { items } = await client.dataset(run.defaultDatasetId).listItems();
  // ... process items ...
  token = items[0]?.pagination_token ?? null;
  pageNumber += 1;
} while (token);
```

A profile with no reactions returns a single sentinel item
`{ "message": "No reaction found for this profile" }`, matching Apify.

## Billing, timeouts & retention

* **Billing**: 1 dataset item = 1 Fetchin credit (the same per-result accounting
  as Apify). A batch of 25 profiles = 25 credits. **Exception:** a post fetched
  with `deepScrape: true` costs **3 credits per post** — the post itself plus the
  two extra requests for its comments and its reactions. A shallow post
  (`deepScrape: false` / omitted) costs 1 credit.
* **Timeout**: the `timeout` input (seconds) bounds a run. If it elapses, the run
  ends `TIMED-OUT` and the items already produced remain in the dataset.
* **Retention**: run records and datasets are retained for 24 hours. Fetch your
  results within that window.
* **Rate**: your account RPS controls how fast we drain your queued items (not
  how many runs you can create). Creating runs far faster than your RPS returns
  `429` with `Retry-After`; the Apify client backs off automatically.

## Differences vs Apify

We validate every actor against real Apify output field-by-field. The reactions,
comments and post actors reach **zero field errors** on matched items; the
differences below are values that either **cannot be reproduced byte-for-byte**
(live counts, signed URLs, per-request tokens) or that we approximate where the
source data isn't exposed. Everything here is stable and documented so you can
decide whether it affects your integration. If a specific field blocks you,
contact us and we'll prioritise it.

### Applies to all actors

* **Live engagement counts** (reactions/comments/reposts totals) drift second to
  second — they reflect the source at fetch time, so two fetches rarely match to
  the digit. This is identical to Apify.
* **Relative timestamps** (`"15h"`, `"2w"`) are wall-clock-relative to the moment
  of the request. Absolute timestamps (`timestamp`, `postedAtISO`, `formatted`)
  are exact.
* **Signed media URLs** carry an expiring signature (`?e=…&v=…&t=…`). The host +
  path are stable; the signature differs every request.
* **Pagination tokens** are opaque and positional — use them, don't compare them.

### `supreme_coder/linkedin-profile-scraper` (profile)

| Field                                                                                                                                                               | Difference                                                                                                                                                                                             |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `currentCompany`                                                                                                                                                    | With `scrapeCompany: true` we return a compact block (`name`, `universalName`, `url`). Apify's full company sub-tree (employee counts, industries, HQ, affiliated pages…) is not fully reproduced yet. |
| `pictureUrl`, `coverImageUrl`, logos                                                                                                                                | Dimension-keyed objects (`{"800x800": "…"}`). We currently populate the largest rendition keyed by its real size; other size keys may be absent.                                                       |
| `courses`, `honors`, `volunteerExperiences`, `projects`, `publications`, `patents`, `testScores`, `organizations`, `certifications`, `languages`, `volunteerCauses` | Returned as `[]` (extraction of these sections is in progress).                                                                                                                                        |
| `mutualConnections`                                                                                                                                                 | Always `[]` — depends on the viewer's network and isn't available to a service account.                                                                                                                |
| `fullName`                                                                                                                                                          | Derived as `firstName + " " + lastName`.                                                                                                                                                               |

### `supreme_coder/linkedin-post` (post)

| Field                                         | Difference                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `author.id`, `authorUrn`                      | The numeric member id / member-scheme urn isn't present in the source feed. We return the public handle (`author.publicId`, `authorProfileId`) and the profile id (`author.profileId`).                                                                                                                                                                                                                                                                                  |
| `author.trackingId`, `author.backgroundImage` | Not reproduced (per-request token / not in the feed).                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `resharedPost`                                | Reshared posts are **not nested**. The underlying feed returns a profile's own posts; pure reshares are filtered.                                                                                                                                                                                                                                                                                                                                                        |
| `url`                                         | Canonical post permalink. The trailing `?rcm=…` token is per-viewer and will differ.                                                                                                                                                                                                                                                                                                                                                                                     |
| `linkedinVideo.videoPlayMetadata`             | Core fields (streams, duration, thumbnail, aspect ratio) are reproduced; `transcripts`, `adaptiveStreams` and `trackingId` are omitted.                                                                                                                                                                                                                                                                                                                                  |
| `attributes`, comment `entities`              | Inline @mention / hashtag annotation ranges are returned as `[]`.                                                                                                                                                                                                                                                                                                                                                                                                        |
| **Deep `comments[]` / `reactions[]`**         | These are **relevance-ranked live lists** — the exact top-N set differs between any two fetches (true on Apify too). Per engager: `firstName`/`lastName` are split best-effort from the display name (Apify uses the source's structured name fields); `publicId` is the profile id (the engager lockup doesn't expose the handle); `id`, `trackingId`, `backgroundImage`, `distance`, `originalLanguage` are omitted. Names, occupation, picture and `profileId` match. |

### `apimaestro/linkedin-profile-comments` (comments)

* **Full field parity** on matched items. Only the volatile values above (live
  comment/post reaction counts, relative time) differ.
* Items do **not** carry a root `pagination_token` — this actor pages by
  `page_number` (this matches Apify's actor).

### `apimaestro/linkedin-profile-reactions` (reactions)

| Field                        | Difference                                                                                                                                                                                                                  |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `action`                     | We return the **reaction type enum** (`LIKE`, `EMPATHY`, `APPRECIATION`, `INTEREST`, `PRAISE`, `ENTERTAINMENT`). Apify emits a header **sentence** (e.g. `"Bill Gates likes this"`). Tell us if you need the sentence form. |
| `post_url`                   | The canonical post permalink — same post, may be a different valid URL form than Apify's.                                                                                                                                   |
| `post_stats` per-type counts | Reproduced (`like`/`appreciation`/`empathy`/`interest`/`praise`), subject to the live-count drift noted above.                                                                                                              |
