# Ultimate Screenshot (`dz_omar/ultimate-screenshot`) Actor

🚀 Capture ANY website as HD screenshots, videos, or PDFs! 📸 100+ device presets (iPhone, Android, tablets). Perfect for web scraping, monitoring, testing & marketing. Export as PNG/JPEG/PDF/MP4/GIF. Fast, reliable, proxy-ready. Try FREE now!

- **URL**: https://apify.com/dz\_omar/ultimate-screenshot.md
- **Developed by:** [FlowExtract API](https://apify.com/dz_omar) (community)
- **Categories:** Automation, SEO tools, Developer tools
- **Stats:** 256 total users, 6 monthly users, 100.0% runs succeeded, 23 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

$13.00/month + usage

To use this Actor, you pay a monthly rental fee to the developer. The rent is subtracted from your prepaid usage every month after the free trial period. You also pay for the Apify platform usage, which gets cheaper the higher Apify subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#rental-actors

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## 📸 Ultimate Screenshot — capture any web page as an image, PDF, video or GIF

**[Ultimate Screenshot](https://apify.com/dz_omar/ultimate-screenshot?fpr=smcx63)** turns a list of web addresses into finished files: JPEG and PNG screenshots, printable PDFs, and short MP4 or GIF recordings of the page. It drives a real browser, so pages that need JavaScript, web fonts, lazy-loaded images or a signed-in session come out looking the way a person would see them — on a desktop window, or on any of **131 phone and tablet presets**.

[![Ultimate Screenshot](https://raw.githubusercontent.com/FlowExtractAPI/Ultimate-Screenshot/refs/heads/main/Ultimate%20Screenshot.png)](https://apify.com/dz_omar/ultimate-screenshot?fpr=smcx63)

Perfect for **agencies** archiving client sites, **QA and product teams** building visual checks, and **marketers** who need a scroll-through clip of a landing page without opening a screen recorder.

***

### What it produces

| Format | What you get | Typical use |
|---|---|---|
| **JPEG** | Compressed image, quality you choose | Bulk archiving, thumbnails, previews |
| **PNG** | Lossless image | Design review, pixel comparisons |
| **PDF** | Printable document — paper size, margins, orientation | Reports, invoices, compliance records |
| **MP4** | Up to a 30-second recording | Landing-page walkthroughs, demos |
| **GIF** | Up to a 10-second looping animation | Social posts, docs, changelog entries |

Every format can be captured **as the visible window** or **as the whole page**, top to bottom.

***

### ⚙️ How to use it

#### The one setting you need

```json
{
    "linkUrls": ["https://example.com/", "https://apify.com/"]
}
```

That captures each page as a JPEG at 1920 × 1080. Everything below is optional.

Addresses go in one per line. A bare domain is fine — `example.com` becomes `https://example.com`. The field checks that each line looks like a web address, so a stray number or word is caught in the form rather than producing an empty result.

#### Full-page screenshot

```json
{
    "linkUrls": ["https://example.com/"],
    "outputFormat": "png",
    "fullPage": true,
    "scrollToBottom": true,
    "maxScrollSeconds": 15
}
```

`scrollToBottom` walks down the page first so lazy-loaded images actually load. `maxScrollSeconds` is the limit that ends the scroll — on an endless feed it is the *only* thing that ends it, and it decides how much of the feed you capture.

#### Phone or tablet

```json
{
    "linkUrls": ["https://example.com/"],
    "device": "iPhone 15 Pro",
    "outputFormat": "jpeg",
    "fullPage": true
}
```

131 presets, each with the right screen size, pixel density and browser identity. A preset replaces the window width and height below it.

#### A scroll-through video

```json
{
    "linkUrls": ["https://example.com/"],
    "outputFormat": "mp4",
    "recordingMode": "scroll",
    "recordSeconds": 10,
    "fps": 15
}
```

The clip is exactly `recordSeconds × fps` frames and plays for exactly `recordSeconds`. In **scroll** mode the step between frames is worked out from the real page height, so the last frame lands at the bottom of the page. In **static** mode the window stays put, which suits carousels, animations and players.

#### A signed-in page

```json
{
    "linkUrls": ["https://example.com/account"],
    "cookies": [
        { "name": "session_id", "value": "abc123", "domain": "example.com" }
    ]
}
```

Export cookies with a browser extension such as [Cookie Editor](https://cookie-editor.com/). Each cookie needs at least a name and a value; the address being captured supplies the domain when it is missing.

#### Cleaning up the page first

```json
{
    "linkUrls": ["https://example.com/"],
    "waitForSelector": "#main-content",
    "hideSelectors": ["#cookie-banner", ".chat-widget", ".sticky-header"],
    "delayBeforeScreenshot": 2000
}
```

#### Finding the right selector

![Right-click the banner, choose Inspect, then read the id or class off the highlighted element](https://raw.githubusercontent.com/FlowExtractAPI/Ultimate-Screenshot/refs/heads/main/find-the-selector-chrome.png)

1. **Right-click the banner** you want gone and choose **Inspect** (bottom of the menu).
2. The developer tools open with that element highlighted. Click **upwards** through the boxes until the highlight covers the **whole banner**, not just its text — you can see the size readout, `340 × 122`, confirming the right box is selected.
3. Read the `id` or `class` off that line and type it with the right prefix:

   | In the HTML | You type | Because |
   |---|---|---|
   | `<div id="onetrust-consent-sdk">` | `#onetrust-consent-sdk` | an **id** takes a **`#`** |
   | `<div class="cookie-bar">` | `.cookie-bar` | a **class** takes a **`.`** |

   Prefer the id when the element has one — it is shorter and changes less often.

> The screenshot is Chrome, but this works the same in **Edge, Firefox and Safari**: you are reading an attribute out of the page's own HTML, not using a browser feature.
>
> Chrome and Edge also offer **Copy → Copy selector** (Firefox: *Copy → CSS Selector*), visible on the right of the screenshot. It works, but it usually produces a long path like `#onetrust-consent-sdk > div:nth-child(2) > div` that breaks the next time the site changes its layout — the plain id above keeps working.

Hide the wrapper, not the text inside it, or you will be left with an empty bar.

Most consent banners come from a handful of platforms, so these cover a lot of the web:

| Platform | Selector |
|---|---|
| OneTrust (used by apify.com and many large sites) | `#onetrust-consent-sdk` |
| Cookiebot | `#CybotCookiebotDialog` |
| Osano | `.osano-cm-window` |
| Quantcast / TCF | `.qc-cmp2-container` |
| Usercentrics | `#usercentrics-root` |
| CookieYes | `.cky-consent-container` |
| Generic catch-all | `[id*="cookie"]`, `[class*="cookie-banner"]` |

Hiding is applied as a style rule as soon as the page opens, so a banner that appears **after** the page has loaded is caught too — many consent scripts take several seconds to inject theirs.

If a selector matches nothing, the result row's `notes` says so rather than leaving you guessing.

***

### 🎛️ All settings

#### Page loading

| Setting | Default | What it does |
|---|---|---|
| `waitUntil` | `load` | When the page counts as ready. `load` suits most sites and is much the fastest complete option. Move to a Network Idle mode only for pages that keep drawing after everything has loaded — it can add many seconds per page. |
| `timeouT` | `30` s | How long to wait for a page before giving up on that attempt. |
| `maxRetries` | `2` | Extra attempts when a page fails to load. `0` means a single attempt. |
| `delayBeforeScreenshot` | `1000` ms | Extra pause after the page is ready, for animations and fonts. |
| `waitForSelector` | — | CSS selector to wait for. If it never appears the page is captured anyway and the row says so. |

#### Scrolling & clean-up

| Setting | Default | What it does |
|---|---|---|
| `scrollToBottom` | `false` | Scroll through the page before capturing so lazy content loads. |
| `maxScrollSeconds` | `15` s | The longest scrolling may take. Scrolling also stops as soon as the page stops growing. |
| `delayAfterScrolling` | `500` ms | Pause after scrolling, so content loaded on the way down can render. |
| `hideSelectors` | — | CSS selectors to hide — cookie banners, chat bubbles, sticky headers. Applied as a style rule the moment the page opens, so it also catches banners that appear seconds later. Selectors matching nothing are reported in `notes`. |

#### Video & GIF

| Setting | Default | What it does |
|---|---|---|
| `recordingMode` | `scroll` | `scroll` pans down the page; `static` holds the window still. |
| `recordSeconds` | `5` s | How long the finished clip plays. GIF max 10, MP4 max 30. |
| `fps` | `10` | Playback frame rate. GIF max 15, MP4 max 30. |

#### Image, PDF, device and network

| Setting | Default | What it does |
|---|---|---|
| `jpegQuality` | `85` | JPEG only. PNG is always lossless. |
| `printBackground` | `true` | PDF: keep background colours and images. |
| `formaT` | `A4` | PDF paper size. Ignored when any margin below is above 0. |
| `landscape` | `false` | PDF orientation. |
| `toP` / `righT` / `bottoM` / `lefT` | `0` mm | PDF margins in **millimetres**. |
| `device` | — | One of 131 phone/tablet presets. Replaces the window size. |
| `window_Width` / `window_Height` | `1920` × `1080` | Browser window when no device preset is chosen. |
| `userAgent` | — | Send a specific browser identity. |
| `cookies` | `[]` | Cookies applied before the page loads. |
| `proxyConfig` | Apify datacenter | Route the capture through a proxy. |
| `enableSSL` | `true` | Turn off only for a self-signed or expired certificate you trust. |

***

### 📊 What you get back

One row per address, plus the file itself in the run's storage.

**Files are grouped by what they are.** Open the run's Storage tab and you get three named collections rather than one flat list:

| Collection | Holds | Key prefix |
|---|---|---|
| **Screenshots** | PNG and JPEG images | `screenshot-` |
| **Videos & GIFs** | MP4 and GIF recordings | `video-` |
| **PDF documents** | PDF renderings | `document-` |

A GIF is filed with video, not with the images — it is a *recording* of the page, made by the same frame capture and described by the same length and frame-rate settings as an MP4. Its file type happens to be `image/gif`, which is why the video collection accepts that type too.

```json
{
    "status": "success",
    "format": "jpeg",
    "linkUrl": "https://example.com/",
    "finalUrl": "https://example.com/",
    "screenshot_url": "https://api.apify.com/v2/key-value-stores/.../records/capture-001-....jpg",
    "screenshot_image": "https://api.apify.com/v2/key-value-stores/.../records/capture-001-....jpg",
    "content_Type": "image/jpeg",
    "fileName": "capture-001-20260904211602-nyyjjkaa.jpg",
    "fileSizeBytes": 24576,
    "pageTitle": "Example Domain",
    "httpStatus": 200,
    "width": 1920,
    "height": 1080,
    "durationSeconds": null,
    "frames": null,
    "captureMs": 2388,
    "error": null,
    "notes": null,
    "capturedAt": "2026-09-04T21:16:02.463Z"
}
```

| Field | What it tells you |
|---|---|
| `status` | `success` when a file was produced, `failed` when the page returned nothing |
| `screenshot_url` / `screenshot_image` | Direct download link for the file |
| `content_Type`, `fileName`, `fileSizeBytes` | What the file is and how big |
| `linkUrl` / `finalUrl` | What you asked for, and where the page ended up after redirects |
| `pageTitle`, `httpStatus` | The page's own title, and the status code it answered with |
| `frames`, `durationSeconds` | Recordings only — how many frames and how long the clip plays |
| `captureMs` | How long this page took, from opening it to saving the file |
| `error` | On a failed page, one sentence saying what happened |
| `notes` | Where the actor tells you it did something you did not ask for |

`screenshot_image`, `content_Type`, `linkUrl` and `screenshot_url` keep the exact names and meanings they had in version 1 — existing integrations need no change.

**`notes` is worth reading.** It is how the actor reports a clipped tall page, a scroll that stopped at the limit, a recording shortened to stay inside the frame limit, or `hideSelectors` that matched nothing.

***

### 💰 Pricing

**$13 / month**, with a **1-day free trial**. Unlimited runs and unlimited captures for the subscription; you pay the Apify platform usage your runs consume, as with any rented Actor.

The settings that move platform usage most:

- **`waitUntil`** is the big one. `load` returned a heavy marketing page in about **5 seconds** where `networkidle0` took **28** — for a screenshot that differed by a few kilobytes.
- **Capture in batches.** Starting a run costs a fixed amount of browser time before the first page opens. Ten addresses in one run use roughly half the platform usage of the same ten as ten separate runs.
- **Recordings cost far more than stills**, in proportion to `recordSeconds × fps`. A 5-second GIF at 10 fps is 50 captures of the page; a 10-second MP4 at 30 fps is 300.

***

### 🛟 Limits, and why they exist

A browser can be asked to do something that never finishes. These limits mean it does not.

| Limit | Value | What happens when it is reached |
|---|---|---|
| Scrolling | your `maxScrollSeconds`, at most 60 s | Scrolling stops, the page is captured as it stands, and a note says so |
| Full-page image height | 16 000 px | The image is clipped to the top 16 000 px — the tallest a browser can render in one pass |
| Recording frames | 300 per page | The clip is shortened, keeping your frame rate, and a note says so |
| Recording time | 2.5 minutes per page | Recording stops and whatever was captured is encoded into a valid clip |
| Whole page | 4 minutes | The page is abandoned and reported; the run carries on with the next address |
| Output file | 100 MB | The page is reported with a suggestion to record less or use a smaller window |

***

### 🔄 If a run is interrupted

Progress is saved after every page. If the run is migrated, restarted or aborted, it resumes from the next uncaptured address instead of starting over — so an interruption never re-captures a page you already received.

***

### 🚫 When something goes wrong

| Situation | What you see | What to do |
|---|---|---|
| An address is not a web address | The run is refused when you submit it, naming the position of the bad line | Fix or remove that line |
| A page will not load | `status: "failed"` and an `error` sentence for that address only | Raise `timeouT`, or check the address in a browser |
| The site answers with an error code | A capture of the error page, and a note giving the status code | Expected — the page really did answer that way |
| A page is taller than 16 000 px | A clipped image, and a note with the page's real height | Capture the window instead of the whole page, or a narrower window |
| A recording came out short | Fewer `frames` than asked, and a note saying why | Lower `fps` or `recordSeconds`, or record a lighter page |
| Nothing was captured at all | A single row with `status: "no_input"` | Check the addresses in the input |

***

### ❓ Frequently asked questions

**Can it capture pages behind a login?**
Yes, with the `cookies` setting. Export the cookies from a signed-in browser session and paste them in.

**Why is my full-page screenshot cut off at the bottom?**
The page is taller than 16 000 pixels. That is a browser limit, not a setting — a browser cannot rasterise an arbitrarily tall page in one image. The row's `notes` gives the page's real height.

**My GIF looks shorter than I asked for.**
Check `frames` and `notes` on the row. A clip is shortened when `recordSeconds × fps` would pass 300 frames, or when the page was too slow to record the full series in time.

**Why is the video not the page's real motion?**
Frames are captured one at a time, so a recording is a time-lapse rather than a real-time screen capture. In `scroll` mode that is exactly what you want — a smooth pass down the page. In `static` mode, fast animations look sped up.

**Can I capture an infinite-scroll feed completely?**
No, and neither can anything else — the feed has no end. Set `maxScrollSeconds` to how much of it you want; that is the amount you get.

**How many pages can I capture per run?**
As many addresses as you like. Batching is more efficient than separate runs.

**What happens if one address fails?**
Only that address. It appears with `status: "failed"` and a sentence saying why, and the run continues.

**I upgraded from version 1 — do I need to change anything?**
No. Every setting from version 1 still works, and the four output fields integrations read keep their exact names. The retired video settings (`frameCounT`, `frame`, `timefullPagE`, `infiniteScroll`) are converted automatically and the run log says what they became. See the changelog for the full list.

**Can I use my own proxy?**
Yes — set your own proxy URLs in the proxy setting. Apify's datacenter proxy is used by default.

***

### ⚖️ Legal & compliance

This actor captures **pages as they are served to a browser** — the same content any visitor can see, plus anything your own cookies give you access to.

Please use it responsibly:

- Only capture pages you are authorised to access, and only use cookies from accounts you own.
- Respect each site's Terms of Service and applicable data-protection law (GDPR, CCPA and equivalents).
- Screenshots of pages containing personal data are personal data — store and share them accordingly.
- Do not use captures to impersonate a site, a person or an organisation.

***

### 🤝 Support

- **📧 Email**: <flowextractapi@outlook.com>
- **🐙 GitHub**: [FlowExtractAPI](https://github.com/FlowExtractAPI)
- **🐦 Twitter**: [@FlowExtractAPI](https://x.com/FlowExtractAPI)
- **🔧 Apify**: [FlowExtract API](https://apify.com/dz_omar?fpr=smcx63)

***

*Built by [FlowExtract API](https://apify.com/dz_omar?fpr=smcx63) — reliable data extraction actors for the Apify platform.*

# Actor input Schema

## `linkUrls` (type: `array`):

The web pages to capture, one address per line. Every address is processed in turn with the settings below. The field now checks that each line looks like a web address, so a stray number or word is caught here instead of producing an empty result. A bare domain is fine — example.com is completed to https://example.com.

## `outputFormat` (type: `string`):

What to produce for each page. JPEG and PNG are still images, PDF is a printable document, MP4 and GIF are short recordings of the page (configured in the Video & GIF section).

## `fullPage` (type: `boolean`):

Capture the entire page from top to bottom instead of just the visible window. Very tall pages are clipped to 16 000 pixels, which is the tallest image a browser can render in one pass. Applies to JPEG and PNG.

## `waitUntil` (type: `string`):

"Load" suits most sites and is the fastest complete option. Choose a Network Idle mode only for pages that keep drawing after everything has loaded — it can add many seconds per page. DOM Ready is fastest of all but may capture a page before its images arrive.

## `timeouT` (type: `integer`):

How long to wait for a page to load before giving up on that attempt. Raise it for slow or heavy sites.

## `maxRetries` (type: `integer`):

Extra attempts to make when a page fails to load. 0 means a single attempt.

## `delayBeforeScreenshot` (type: `integer`):

Extra pause after the page is ready and before the capture starts. Useful for animations, fonts and lazy images that need a moment to settle.

## `waitForSelector` (type: `string`):

Optional CSS selector to wait for before capturing, for example "#main" or ".product-grid". If it never appears the page is captured anyway, and the result row says so.

## `scrollToBottom` (type: `boolean`):

Scroll to the bottom before capturing, so lazy-loaded images and content are actually there. Recommended whenever "Capture the whole page" is on.

## `maxScrollSeconds` (type: `integer`):

The longest scrolling is allowed to take. Scrolling also stops as soon as the page stops growing. On an endless feed — a social timeline, a news river — this limit is what ends the scroll, and it decides how much of the page ends up in the capture.

## `delayAfterScrolling` (type: `integer`):

Pause after scrolling finishes, so content loaded on the way down has time to render.

## `hideSelectors` (type: `array`):

One selector per line. Anything matching is hidden before the shot — cookie banners, chat bubbles, sticky headers, newsletter pop-ups.

Hiding is applied the moment the page opens, so banners that appear a few seconds later are caught too. A selector that matches nothing is reported in the result row's `notes`, so a typo never fails silently.

## `recordingMode` (type: `string`):

Scroll through the page records a smooth top-to-bottom pass; the step between frames follows the real page height, so the last frame lands exactly at the bottom. Stay in place records the visible window without moving, which suits carousels, animations and players.

## `recordSeconds` (type: `integer`):

How long the finished clip plays for. GIFs are capped at 10 seconds and MP4s at 30. A clip is shortened automatically if length × frame rate would pass 300 frames, and the result row says when that happened.

## `fps` (type: `integer`):

Playback frame rate. 10 is smooth for a scroll-through, 24–30 looks like video. GIFs are capped at 15. Higher values mean more frames, a longer capture and a bigger file.

## `jpegQuality` (type: `integer`):

Higher is sharper and larger. 85 is a good balance for screenshots of text-heavy pages.

## `printBackground` (type: `boolean`):

Keep background colours and images, so the PDF looks like the page rather than a printer-friendly version of it.

## `formaT` (type: `string`):

Standard paper size for the PDF. Ignored when any custom margin below is above 0.

## `landscape` (type: `boolean`):

Rotate the PDF pages to landscape.

## `toP` (type: `integer`):

Top margin in millimetres. Setting any margin above 0 replaces the paper size above with these exact margins.

## `righT` (type: `integer`):

Right margin in millimetres.

## `bottoM` (type: `integer`):

Bottom margin in millimetres.

## `lefT` (type: `integer`):

Left margin in millimetres.

## `device` (type: `string`):

Emulate a specific phone or tablet, including its screen size, pixel density and browser identity. Leave empty to use the custom window size below.

## `window_Width` (type: `integer`):

Browser window width in pixels when no device preset is selected. 1920 is a standard desktop; 1366 and 1440 are common laptop widths.

## `window_Height` (type: `integer`):

Browser window height in pixels when no device preset is selected. With "Capture the whole page" on, this only sets the starting window before scrolling.

## `userAgent` (type: `string`):

Optional User-Agent string to send instead of the default one. Leave empty unless a site needs a specific browser identity.

## `cookies` (type: `array`):

🔒 Encrypted at rest and never written to the run log — only the number of cookies applied is reported.

Paste a JSON array exported from a browser where you are signed in. Cookie-Editor's JSON export works as-is: `expirationDate`, `sameSite: "no_restriction"`, `hostOnly` and a leading dot on the domain are all understood.

**How to get them**

1. Install [Cookie-Editor](https://cookie-editor.com) or [Copy Cookies](https://chromewebstore.google.com/detail/copy-cookies/jcbpglbplpblnagieibnemmkiamekcdg)
2. Sign in to the site in your browser
3. Open the extension → Export → JSON
4. Paste the array here

Each cookie needs at least a name and a value; the address being captured supplies the domain when it is missing. If the site clears the session during load, the result row says so.

## `proxyConfig` (type: `object`):

Which proxy to use. Leave the default unless you need a specific country or your own proxy.

## `enableSSL` (type: `boolean`):

Keep this on for normal use. Turn it off only to capture a page with a self-signed or expired certificate that you trust.

## Actor input object example

```json
{
  "linkUrls": [
    "https://apify.com/"
  ],
  "outputFormat": "jpeg",
  "fullPage": false,
  "waitUntil": "load",
  "timeouT": 30,
  "maxRetries": 2,
  "delayBeforeScreenshot": 1000,
  "scrollToBottom": false,
  "maxScrollSeconds": 15,
  "delayAfterScrolling": 500,
  "recordingMode": "scroll",
  "recordSeconds": 5,
  "fps": 10,
  "jpegQuality": 85,
  "printBackground": true,
  "formaT": "A4",
  "landscape": false,
  "toP": 0,
  "righT": 0,
  "bottoM": 0,
  "lefT": 0,
  "window_Width": 1920,
  "window_Height": 1080,
  "proxyConfig": {
    "useApifyProxy": true
  },
  "enableSSL": true
}
```

# Actor output Schema

## `results` (type: `string`):

Every capture with its preview and download link.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "linkUrls": [
        "https://apify.com/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("dz_omar/ultimate-screenshot").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "linkUrls": ["https://apify.com/"] }

# Run the Actor and wait for it to finish
run = client.actor("dz_omar/ultimate-screenshot").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "linkUrls": [
    "https://apify.com/"
  ]
}' |
apify call dz_omar/ultimate-screenshot --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dz_omar/ultimate-screenshot"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/FU5kPkREa2rdypuqb/builds/QmkUvyESajkRvhWaC/openapi.json
