How to Use the Browshot Screenshot API with a WordPress Website
Connect Browshot to WordPress with a server-side PHP integration. Create screenshots, handle asynchronous status, and render the result safely.
Direct answer: use a custom WordPress plugin or server-side PHP component to call Browshot through WordPress’s HTTP API. Keep the Browshot API key on the server, send the target url and an instance_id, then handle the response as either a completed screenshot, an in-progress job, or an error. WordPress does not need its REST API for this: WordPress itself can call Browshot from PHP. The example below is a suggested implementation based on the platforms’ documentation; it is not an official Browshot plugin or a tested integration. Browshot API documentation · WordPress HTTP API handbook.
1. Choose a Browshot request flow
Browshot documents both a simple endpoint and a complete screenshot API. The simple API is easier to call, but Browshot describes it as slower. The complete API creates a screenshot job and lets you check its status and retrieve its image. For a WordPress feature that needs to report progress or store the resulting image, the complete flow gives you clearer status handling.
| Flow | Use it when | What to handle |
|---|---|---|
| Simple API | You need a minimal request and can follow redirects. | Image response, error response, or redirect while the screenshot is processing. Browshot says some pages can take up to two minutes. |
| Complete screenshot API | You are building a plugin workflow, admin action, or queued task. | Create response, screenshot ID, status checks through screenshot/info, then the finished screenshot URL. |
This is a comparison of the documented request flows, not a speed benchmark. The implementation below uses the complete API and returns a status object rather than blocking a WordPress request while a page renders.
2. Get the API key and choose where to store it
- Create or sign in to a Browshot account and copy the API key shown in its dashboard.
- Choose the Browshot instance ID to use. The create request requires
instance_id; do not assume an instance is available to your account without checking your Browshot account and current service terms. - Store the key on the server. A deployment environment constant or server-managed secret is preferable to a public page, theme JavaScript, or a shortcode attribute.
For example, define secrets in a server-managed configuration and make them available to PHP as environment variables. You can also define a constant in wp-config.php, outside the publicly served document root where your hosting setup permits it:
define( 'BROWSHOT_API_KEY', 'replace-with-your-server-side-key' );
define( 'BROWSHOT_INSTANCE_ID', 12 );
The instance ID above is an example only. Do not commit a live key to source control. Rotate a key if it has been exposed.
3. Add a small server-side WordPress integration
Create wp-content/plugins/browshot-capture/browshot-capture.php, paste the following code, set the key and instance ID in server configuration, and activate Browshot Capture in the WordPress Plugins screen. This plugin exposes an authenticated shortcode: an administrator can use [browshot_capture url="https://example.com/page"] in a post or page. Restricting it to administrators is a safe starting point because visitors should not be able to trigger external captures freely.
<?php
/**
* Plugin Name: Browshot Capture
* Description: Requests a Browshot screenshot from an administrator-only shortcode.
* Version: 1.0.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
function bsc_api_key() {
return defined( 'BROWSHOT_API_KEY' ) ? BROWSHOT_API_KEY : '';
}
function bsc_instance_id() {
return defined( 'BROWSHOT_INSTANCE_ID' ) ? absint( BROWSHOT_INSTANCE_ID ) : 0;
}
/** Send a JSON API request using WordPress HTTP functions. */
function bsc_api_get( $path, $params ) {
$params['key'] = bsc_api_key();
$url = add_query_arg( $params, 'https://api.browshot.com/api/v1/' . ltrim( $path, '/' ) );
$response = wp_remote_get( $url, array(
'timeout' => 20,
'redirection' => 3,
'limit_response_size' => 1024 * 1024,
'headers' => array( 'Accept' => 'application/json' ),
) );
if ( is_wp_error( $response ) ) {
return $response;
}
$code = wp_remote_retrieve_response_code( $response );
$body = wp_remote_retrieve_body( $response );
$data = json_decode( $body, true );
if ( $code < 200 || $code >= 300 ) {
return new WP_Error( 'bsc_http_error', 'Browshot returned HTTP ' . absint( $code ) . '.' );
}
if ( ! is_array( $data ) ) {
return new WP_Error( 'bsc_invalid_json', 'Browshot returned an unreadable JSON response.' );
}
if ( isset( $data['status'] ) && 'error' === $data['status'] ) {
$message = isset( $data['error'] ) ? sanitize_text_field( $data['error'] ) : 'Screenshot request failed.';
return new WP_Error( 'bsc_provider_error', $message );
}
return $data;
}
/** Start one screenshot job; completion is checked separately. */
function bsc_start_screenshot( $page_url, $options = array() ) {
if ( ! bsc_api_key() || ! bsc_instance_id() ) {
return new WP_Error( 'bsc_missing_config', 'Set the Browshot API key and instance ID on the server.' );
}
$page_url = esc_url_raw( $page_url, array( 'http', 'https' ) );
if ( ! $page_url || ! wp_http_validate_url( $page_url ) ) {
return new WP_Error( 'bsc_invalid_url', 'Enter a valid public HTTP or HTTPS page URL.' );
}
$allowed_size = isset( $options['size'] ) && 'page' === $options['size'] ? 'page' : 'screen';
$params = array(
'url' => $page_url,
'instance_id'=> bsc_instance_id(),
'size' => $allowed_size,
'cache' => isset( $options['cache'] ) ? absint( $options['cache'] ) : 86400,
'delay' => isset( $options['delay'] ) ? min( 20, absint( $options['delay'] ) ) : 5,
);
return bsc_api_get( 'screenshot/create', $params );
}
/** Look up a job by its Browshot screenshot ID. */
function bsc_get_screenshot_status( $screenshot_id ) {
$screenshot_id = absint( $screenshot_id );
if ( ! $screenshot_id ) {
return new WP_Error( 'bsc_invalid_id', 'A valid screenshot ID is required.' );
}
return bsc_api_get( 'screenshot/info', array( 'id' => $screenshot_id ) );
}
/** Admin-only example UI: start a job and print its status or image. */
function bsc_capture_shortcode( $attributes ) {
if ( ! current_user_can( 'manage_options' ) ) {
return '<p>An administrator must run this screenshot action.</p>';
}
$attributes = shortcode_atts( array( 'url' => '', 'size' => 'screen' ), $attributes, 'browshot_capture' );
$result = bsc_start_screenshot( $attributes['url'], array( 'size' => $attributes['size'] ) );
if ( is_wp_error( $result ) ) {
return '<p>Screenshot request error: ' . esc_html( $result->get_error_message() ) . '</p>';
}
if ( isset( $result['status'] ) && 'finished' === $result['status'] && ! empty( $result['screenshot_url'] ) ) {
return '<img src="' . esc_url( $result['screenshot_url'] ) . '" alt="Screenshot of ' . esc_attr( $attributes['url'] ) . '" loading="lazy" />';
}
if ( isset( $result['id'] ) ) {
return '<p>Screenshot request accepted. Job ID: ' . absint( $result['id'] ) . '. Check this ID with bsc_get_screenshot_status() from a scheduled task or admin action.</p>';
}
return '<p>Browshot returned an unexpected response. Check server logs and the API response handling.</p>';
}
add_shortcode( 'browshot_capture', 'bsc_capture_shortcode' );
The sample returns an accepted job ID instead of polling until completion during page rendering. That keeps an admin page from waiting through a slow target page. To show the eventual image, store the returned ID in post meta or a custom table, then have an authenticated admin action or scheduled background task call bsc_get_screenshot_status() and save the finished screenshot_url. Escaping the URL at output time matters; do not echo provider data into HTML unchecked.
Important production adjustment: restrict target URLs
The administrator check prevents public visitors from triggering captures, but a production plugin should also restrict which domains administrators can submit if users with that capability are not fully trusted. A screenshot endpoint can otherwise be pointed at unintended destinations. Use an allowlist appropriate to your site, validate submitted URLs, and do not make an unauthenticated public endpoint that accepts arbitrary URLs.
4. Understand the API parameters and responses
The create request requires url and instance_id. Browshot documents these commonly used controls; verify supported options for the instance and API version you use:
| Parameter | Purpose | Practical note |
|---|---|---|
size |
screen captures the viewport; page requests the full page. |
Full-page captures can take longer and produce larger image files. |
cache |
Reuse a screenshot of the same URL and instance made within the specified number of seconds. | Documentation gives a 24-hour default and cache=0 to request a fresh capture. Cache policy affects freshness. |
delay |
Wait after page load before capture. | The documented range is 0–20 seconds, with a default of 5. Use more delay only when content renders after load. |
screen_width, screen_height |
Set the desktop viewport dimensions. | Use values supported by the selected instance; viewport size changes responsive layout. |
html |
Ask Browshot to capture rendered HTML alongside the screenshot. | The documentation states this costs one credit per screenshot. |
| Headers, JavaScript, CSS selector and other controls | Customize page requests and capture behavior. | Check the API docs for exact accepted names and constraints before adding them. |
The create endpoint may return {"status":"in_process","id":5} or a finished response with screenshot_url. Poll /api/v1/screenshot/info?id=...&key=... until status is finished or error. Use bounded retries with an interval, such as several seconds, and a deadline; do not create a new screenshot job on every status check. If the initial response is already finished (for example, a cache hit), skip polling.
5. Minimal cURL, Python, and Node.js examples
These direct examples call Browshot’s complete API. They demonstrate the same create-and-check flow outside WordPress. Replace the key, target URL, and instance ID with values from your account. In application code, do not put a secret in a browser request or a public repository.
cURL
curl --get 'https://api.browshot.com/api/v1/screenshot/create' \
--data-urlencode 'url=https://example.com/' \
--data-urlencode 'instance_id=12' \
--data-urlencode 'size=screen' \
--data-urlencode 'key=YOUR_BROWSHOT_API_KEY'
The response is JSON. If it contains an ID with in_process, check status:
curl --get 'https://api.browshot.com/api/v1/screenshot/info' \
--data-urlencode 'id=SCREENSHOT_ID' \
--data-urlencode 'key=YOUR_BROWSHOT_API_KEY'
Python
import time
import requests
API = "https://api.browshot.com/api/v1"
KEY = "YOUR_BROWSHOT_API_KEY"
response = requests.get(
f"{API}/screenshot/create",
params={"url": "https://example.com/", "instance_id": 12, "size": "screen", "key": KEY},
timeout=30,
)
response.raise_for_status()
job = response.json()
if job.get("status") == "error":
raise RuntimeError(job.get("error", "Browshot screenshot failed"))
for _ in range(18): # bounded polling: at most about three minutes
if job.get("status") in ("finished", "error"):
break
screenshot_id = job.get("id")
if not screenshot_id:
raise RuntimeError(f"Unexpected Browshot response: {job}")
time.sleep(10)
status_response = requests.get(
f"{API}/screenshot/info",
params={"id": screenshot_id, "key": KEY},
timeout=30,
)
status_response.raise_for_status()
job = status_response.json()
if job.get("status") == "error":
raise RuntimeError(job.get("error", "Browshot screenshot failed"))
if job.get("status") != "finished" or not job.get("screenshot_url"):
raise TimeoutError("Screenshot is still processing; check its ID again later")
image_response = requests.get(job["screenshot_url"], timeout=60)
image_response.raise_for_status()
with open("browshot.png", "wb") as image_file:
image_file.write(image_response.content)
print("Saved browshot.png")
Node.js
const API = 'https://api.browshot.com/api/v1';
const KEY = 'YOUR_BROWSHOT_API_KEY';
async function browshotGet(path, params) {
const query = new URLSearchParams({ ...params, key: KEY });
const response = await fetch(`${API}/${path}?${query}`);
if (!response.ok) throw new Error(`Browshot HTTP ${response.status}`);
return response.json();
}
const job = await browshotGet('screenshot/create', {
url: 'https://example.com/', instance_id: '12', size: 'screen'
});
if (job.status === 'error') throw new Error(job.error || 'Screenshot failed');
let current = job;
for (let attempt = 0; current.status !== 'finished' && current.status !== 'error' && attempt < 18; attempt++) {
if (!current.id) throw new Error(`Unexpected Browshot response: ${JSON.stringify(current)}`);
await new Promise(resolve => setTimeout(resolve, 10000));
current = await browshotGet('screenshot/info', { id: String(current.id) });
}
if (current.status === 'error') throw new Error(current.error || 'Screenshot failed');
if (current.status !== 'finished' || !current.screenshot_url) {
throw new Error('Screenshot is still processing; check its ID again later');
}
const image = await fetch(current.screenshot_url);
if (!image.ok) throw new Error(`Image download HTTP ${image.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('browshot.png', Buffer.from(await image.arrayBuffer()));
console.log('Saved browshot.png');
6. Simple API alternative
For a one-off image response, the simple endpoint can return the image directly. Browshot documents that it may respond with redirects while processing, and some pages can take up to two minutes. A command-line request that follows redirects looks like this:
curl -L --get 'https://api.browshot.com/api/v1/simple' \
--data-urlencode 'url=https://example.com/' \
--data-urlencode 'instance_id=12' \
--data-urlencode 'key=YOUR_BROWSHOT_API_KEY' \
--output screenshot.png
Do not assume every HTTP response body is a valid screenshot. Inspect the status and content type, and handle a not-found result or API error. In WordPress, the complete API is easier to integrate when you want to persist job IDs and provide an explicit progress state. Browshot documents HTTP outcomes for the simple endpoint including 200, 302, 400, and 404; follow 302/307 redirects for processing where applicable.
7. Display and store the finished image
A finished response includes a screenshot_url. You have two common choices:
- Display from the returned URL: simplest, but rendering depends on the provider URL remaining available and may expose a URL containing an API key. Avoid publishing a secret-bearing URL without checking its exact form and access behavior.
- Download into the WordPress Media Library: better when you need a durable local asset, but adds storage, cleanup, and retention responsibilities. Fetch only a URL returned by Browshot over HTTPS, check the content type and size, and use WordPress upload APIs rather than writing arbitrary files.
The documentation supports screenshot creation, status lookup, and image retrieval; it does not prescribe a WordPress shortcode, admin screen, storage schema, or media workflow. Those are choices for your integration. Keep a record mapping your WordPress post or task to Browshot’s screenshot ID so retries do not accidentally create duplicate jobs.
8. Performance, reliability, and cost
- Keep capture out of frontend page loads. Prefer an admin action, queue, or scheduled task. A remote website can be slow; Browshot’s simple API documentation notes that some pages may take up to two minutes.
- Use cache deliberately. Browshot documents a default cache window of 24 hours and a configurable number of seconds. Caching can reduce repeat work for an unchanged URL; use
cache=0when you explicitly require a fresh capture. - Bound network waits and retries. Set a WordPress HTTP timeout, poll with a maximum attempt count, and retain the job ID so a later task can resume status checks. Retry temporary transport failures with backoff, but avoid blindly resubmitting create requests.
- Control image size. Viewport screenshots generally produce less data than full-page images. Request
size=pageonly when the full document is needed. - Budget credits using current account terms. The documentation reviewed says the free instance is limited to 100 screenshots per month and that optional HTML capture costs one credit per screenshot. These service limits can change, so check Browshot’s current pricing and account documentation before relying on quotas.
- Log carefully. Record job IDs, status, timing, and sanitized error codes. Do not log the API key or full URLs when they contain private query parameters or personal data.
9. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| “Invalid key” or authentication error | Key is missing, mistyped, revoked, or not being sent as key. |
Check the server constant and Browshot dashboard. Keep the secret server-side. |
| “Not enough credits” | The selected instance or account requires credits and the balance is insufficient. | Check instance and account balance in Browshot before retrying. |
| WordPress reports a timeout | The target page or screenshot job took longer than the HTTP timeout. | Use the asynchronous create/status flow; do not lengthen a visitor-facing request indefinitely. |
| JSON parse error or unexpected HTML | A proxy, host firewall, redirect, or upstream error returned non-JSON content. | Inspect the HTTP status and a safely truncated response body in server logs. Confirm the host can reach api.browshot.com. |
Job remains in_process |
Capture is still running, or the target is slow or waits for client-side content. | Check again after a delay, increase the documented delay only if needed, and apply a bounded deadline. |
| Screenshot is blank or incomplete | Content loads after capture, requires interaction, or is blocked in the capture environment. | Try a suitable delay or documented JavaScript/steps options. Check the final URL and page behavior; do not assume a screenshot API can access a page requiring credentials or permissions. |
| Wrong responsive layout | Viewport dimensions or instance differ from the expected browser context. | Set supported screen_width and screen_height values and verify the chosen instance. |
| Simple endpoint saves an error page as an image | The response was not validated as an image. | Check HTTP status and content type before saving; use the complete API for explicit JSON status handling. |
| Shortcode says URL is invalid | URL lacks a scheme, is malformed, or fails WordPress URL validation. | Use a complete public https:// URL and ensure the site can reach it. |
10. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. Its one-request API returns a screenshot or PDF, and its parameters include the names used by other screenshot APIs, which can make switching easier. Learn about ScreenshotNeo.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
See the ScreenshotNeo API documentation for the request options. It removes cookie banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, failed loads, and cache hits are not billed. Its MCP server gives AI agents tools to take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000.
Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.
FAQ
Is there an official Browshot WordPress plugin?
The sources reviewed establish Browshot’s API and WordPress’s HTTP functions, but do not establish an official Browshot WordPress plugin. Treat the code here as a custom integration pattern.
Should WordPress call Browshot through the WordPress REST API?
No. A PHP plugin can call Browshot directly with WordPress’s HTTP API. WordPress’s REST API is useful if another client needs to call your WordPress site, but it is not required for this server-to-server request.
Can I capture a page behind a login?
Browshot documents controls such as custom headers and browser steps. Only automate pages you are authorized to access, and avoid exposing credentials in public posts, logs, or client-side code.
Does a completed create response always include the image bytes?
The complete API returns status data and a screenshot URL for a finished screenshot. Retrieve the image using that URL or the documented image retrieval operation.
Can I use the free instance for every WordPress capture?
Instance availability, quotas, and account terms may change. Check Browshot’s current dashboard and documentation for your account.
Sources
- Browshot API documentation — simple and complete APIs, screenshot creation and info, parameters, statuses, and service notes.
- WordPress HTTP API handbook — server-side HTTP request functions.
- WordPress
wp_remote_get()reference and response code helper.


