ScreenshotNeo

BlogHow-to

How to Use the Microlink API to Screenshot a Webpage in PHP

Call Microlink’s screenshot API from PHP with cURL, read the returned image URL, and customize captures for full pages, elements, or direct image output.

By the ScreenshotNeo team4 October 20268 min read

To screenshot a webpage in PHP with Microlink, send an HTTP GET request to https://api.microlink.io/ with the target page in url and screenshot=true. Microlink returns JSON; the hosted screenshot URL is at data.screenshot.url. The example below uses PHP’s cURL extension and checks both the HTTP response and Microlink’s JSON status before using that URL.

1. Send a screenshot request from PHP

Requirements: PHP with the cURL extension enabled. Save this as microlink-shot.php and run it from the command line. It prints the screenshot URL when the request succeeds.

<?php

$baseUrl = 'https://api.microlink.io/';
$params = [
    'url' => 'https://example.com',
    'screenshot' => 'true',
];

$requestUrl = $baseUrl . '?' . http_build_query($params);
$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL => $requestUrl,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
]);

$response = curl_exec($curl);
$curlError = curl_error($curl);
$httpStatus = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);

if ($response === false) {
    throw new RuntimeException('Microlink request failed: ' . $curlError);
}

$result = json_decode($response, true);
if ($httpStatus < 200 || $httpStatus >= 300 || !is_array($result) || ($result['status'] ?? null) !== 'success') {
    throw new RuntimeException('Microlink did not return a successful screenshot response. HTTP status: ' . $httpStatus);
}

$screenshotUrl = $result['data']['screenshot']['url'] ?? null;
if (!$screenshotUrl) {
    throw new RuntimeException('The response did not include data.screenshot.url.');
}

echo $screenshotUrl . PHP_EOL;

Microlink’s documented PHP example uses this endpoint, http_build_query, cURL, and a 30-second timeout. The checks for transport errors, HTTP status, JSON validity, and the expected field make the example safer to use in application code. See the Microlink screenshot parameter reference and API guide.

Save the screenshot locally

The JSON response contains a hosted image URL. If your application needs a local file, fetch that URL separately and write the bytes to disk. Check the second request too; a successful API JSON response does not guarantee that a later download will succeed.

<?php

// Assume $screenshotUrl came from data.screenshot.url after validating the API response.
$image = file_get_contents($screenshotUrl);
if ($image === false) {
    throw new RuntimeException('Could not download the screenshot image.');
}

if (file_put_contents(__DIR__ . '/screenshot.png', $image) === false) {
    throw new RuntimeException('Could not write screenshot.png.');
}

For production code, use cURL for the image download as well if you need explicit timeouts, HTTP status checks, or custom retry handling. Choose a filename and extension that match the requested image type.

Use the URL in an image element

If you need the image in HTML rather than JSON metadata, Microlink documents embed=screenshot.url. This makes the API respond with the image directly, which can be convenient for an <img> source or CSS background. Keep the default JSON response when your PHP code needs to inspect the screenshot URL or its metadata.

<?php

$params = [
    'url' => 'https://example.com',
    'screenshot' => 'true',
    'embed' => 'screenshot.url',
];
$directImageUrl = 'https://api.microlink.io/?' . http_build_query($params);

echo '<img src="' . htmlspecialchars($directImageUrl, ENT_QUOTES, 'UTF-8') . '" alt="Webpage screenshot">';

Microlink’s screenshot parameters are passed as query parameters. Use dot notation for nested screenshot settings in a direct URL request. Build the query with http_build_query so values such as URLs and CSS selectors are encoded correctly.

Need Parameter Example
Capture the scrollable page screenshot.fullPage true
Capture one element screenshot.element #section-hero
Choose an image encoding screenshot.type png or jpeg
Skip metadata extraction meta false

Full-page capture

$params = [
    'url' => 'https://example.com',
    'screenshot' => 'true',
    'screenshot.fullPage' => 'true',
];

A full-page image can be much taller and larger than a viewport screenshot. Use it when the content below the fold matters, and account for the resulting file size and download time.

Capture one element

$params = [
    'url' => 'https://example.com',
    'screenshot' => 'true',
    'screenshot.element' => '#section-hero',
];

The selector must match an element on the rendered page. If it is dynamic, ensure the page has time to render it; a selector that does not exist cannot identify the intended region. The documented option accepts a CSS selector.

Choose PNG or JPEG

$params = [
    'url' => 'https://example.com',
    'screenshot' => 'true',
    'screenshot.type' => 'png',
];

The screenshot guide documents PNG and JPEG. PNG is useful when preserving sharp edges and interface details matters; JPEG can suit photographic content where a smaller image is preferable. Confirm the returned metadata and use a matching local file extension.

Skip metadata for screenshot-only requests

$params = [
    'url' => 'https://example.com',
    'screenshot' => 'true',
    'meta' => 'false',
];

If the application only needs the screenshot, meta=false skips metadata extraction. Microlink describes this as usually the biggest speedup for screenshot-only requests. Keep metadata enabled if your workflow uses other extracted page information.

3. cURL, Python, and Node.js equivalents

These examples request the same page and return the Microlink JSON response. The API’s screenshot URL remains in data.screenshot.url.

cURL

curl -G 'https://api.microlink.io/' \
  --data-urlencode 'url=https://example.com' \
  --data-urlencode 'screenshot=true'

Python

import requests

response = requests.get(
    "https://api.microlink.io/",
    params={"url": "https://example.com", "screenshot": "true"},
    timeout=30,
)
response.raise_for_status()
data = response.json()
if data.get("status") != "success":
    raise RuntimeError(f"Microlink did not succeed: {data}")
screenshot_url = data["data"]["screenshot"]["url"]
print(screenshot_url)

Node.js

const params = new URLSearchParams({
  url: 'https://example.com',
  screenshot: 'true',
});

const response = await fetch(`https://api.microlink.io/?${params}`, {
  signal: AbortSignal.timeout(30_000),
});
if (!response.ok) {
  throw new Error(`Microlink HTTP error: ${response.status}`);
}
const data = await response.json();
if (data.status !== 'success' || !data.data?.screenshot?.url) {
  throw new Error('Microlink response did not contain a screenshot URL');
}
console.log(data.data.screenshot.url);

4. Authentication, quotas, and safe key handling

Microlink’s service overview says its free endpoint allows 25 requests per day without an API key. Its overview documents Pro authentication through the x-api-key request header. Keep keys on the server and out of browser JavaScript, public repositories, and URLs. Plan limits and pricing can change, so check Microlink’s current service page before choosing a plan.

$apiKey = getenv('MICROLINK_API_KEY');
if (!$apiKey) {
    throw new RuntimeException('Set MICROLINK_API_KEY in the server environment.');
}

curl_setopt($curl, CURLOPT_HTTPHEADER, ['x-api-key: ' . $apiKey]);

Add the header to the cURL handle before calling curl_exec. Avoid logging the key or returning it to a browser. The no-key daily allowance is suitable for trying the endpoint, but applications with recurring traffic should track usage against the live plan limits.

5. Troubleshooting common failures

Symptom Likely cause What to check
curl_exec returns false Network, TLS, DNS, or timeout failure between PHP and the API. Read curl_error(); verify outbound HTTPS works and increase the timeout only if the request legitimately needs more time.
HTTP error or non-success JSON status The request was rejected, quota or authentication may be involved, or capture processing failed. Record the HTTP status and response body safely; check query parameters, credentials, and current account limits.
Missing data.screenshot.url The response did not contain the screenshot data expected by the application. Check that screenshot=true is present and the top-level status is successful before dereferencing fields.
Invalid JSON The response may be an intermediary error page or another non-JSON body. Inspect the HTTP status and a bounded, redacted portion of the response; do not assume every response is JSON.
Screenshot appears blank or incomplete The target may render slowly, require client-side state, or have content outside the viewport. Try full-page capture when content is below the fold; confirm the page is publicly reachable and ready when captured.
Element capture does not work The CSS selector may not match the rendered DOM. Check the selector against the live page and confirm the element appears before capture.
Daily limit reached The no-key endpoint has a documented free allowance, which may be exhausted. Review the current plan information and use the documented API key header for an eligible plan.
Image download fails after the API succeeded The hosted asset request failed or local storage is unavailable. Check the image URL response separately, add a timeout, and verify the destination directory is writable.

6. Performance, reliability, and cost

  • Reduce work when possible: set meta=false for screenshot-only jobs. Request a viewport capture rather than a full page if that is all the feature needs.
  • Set a finite timeout: the sample uses 30 seconds. Treat timeout as a failed request and decide whether a bounded retry fits the user-facing latency budget.
  • Avoid blind retries: retry only transient transport or server failures, with a small retry limit and backoff. A malformed request or exhausted quota needs correction, not repeated calls.
  • Separate capture from image download: the API JSON call and the subsequent hosted image fetch are two network operations, each with its own failure path.
  • Control concurrency: queue large batches and cap simultaneous requests to stay within the account’s current limits and your application’s resource budget.
  • Check current plan terms: the service overview states 25 free requests per day without a key; quotas, pricing, and plan details are vendor-controlled and can change.

No independent comparative benchmark is needed to use this API, and this guide makes no performance guarantee. Actual completion time depends on the target page, response path, and capture settings.

7. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. Its API takes one GET request and returns an image or PDF. The cURL example below saves a WebP screenshot. Read the ScreenshotNeo API documentation for request options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Equivalent PHP using cURL:

<?php

$params = [
    'access_key' => getenv('SCREENSHOTNEO_API_KEY'),
    'url' => 'https://stripe.com',
];
$curl = curl_init('https://api.screenshotneo.com/v1/shot?' . http_build_query($params));
curl_setopt_array($curl, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 90,
]);
$image = curl_exec($curl);
$error = curl_error($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);

if ($image === false || $status < 200 || $status >= 300) {
    throw new RuntimeException('ScreenshotNeo request failed: ' . $error . ' (HTTP ' . $status . ')');
}
file_put_contents(__DIR__ . '/shot.webp', $image);

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, and failed loads are not billed; an MCP server lets AI agents take screenshots; and 1,000 screenshots per month are free with no card. Paid plans start at $5 for 3,000 screenshots. Sign up for 1,000 free screenshots a month with no card.

FAQ

How do I take a webpage screenshot in PHP?

Send a GET request to Microlink’s API with url set to the page and screenshot=true, then read data.screenshot.url from the successful JSON response.

Decode the JSON response as an associative PHP array and read $result['data']['screenshot']['url'] after checking the response status.

Can the API return an image instead of JSON?

Yes. Microlink documents embed=screenshot.url for a direct image response. Use the default JSON response when your program needs the hosted URL and metadata.

Does a full-page screenshot include content below the fold?

Use screenshot.fullPage=true to request the scrollable page rather than only the default viewport.

Can I use the free endpoint without an API key?

Microlink’s service overview currently states a 25-request daily allowance without a key. Verify the live service page for current limits before relying on it.