ScreenshotNeo

BlogHow-to

How to Apply Custom Rules to Website Screenshot and Metadata APIs

Inject CSS or JavaScript, target elements, and wait for dynamic content when using screenshot APIs. Learn how to verify provider-specific options and handle metadata separately.

By the ScreenshotNeo team4 October 202610 min read

Apply custom rendering rules through the screenshot API request: use its documented CSS or JavaScript injection options, selectors to hide or capture elements, and a wait condition that matches the page state you need. Parameter names and limits vary by provider, so check the endpoint reference before adapting an example. Metadata extraction is a separate capability; styling a screenshot does not itself return page title, Open Graph data, or other metadata.

This guide covers the integration choices that matter: how to inject rules, target elements, wait for dynamic content, handle metadata, and troubleshoot inconsistent output.

1. Check the API contract before writing the request

Start with the selected provider’s endpoint documentation. Do not assume a parameter name or HTTP method works across services. For example, Screenshot API documents POST-only css, js, and hideSelectors; ShotAPI uses custom_css, custom_js, and hide_selectors; Cloudflare Browser Run uses addStyleTag and addScriptTag. ShotAPI also documents a 10 KB limit for injected CSS and JavaScript and marks the listed premium options as paid-plan features. Screenshot API documentation, ShotAPI documentation, and Cloudflare Browser Run documentation describe their respective request formats.

  1. Confirm the endpoint’s HTTP method and content type.
  2. Find the exact option names for CSS, JavaScript, hide selectors, capture selectors, and waits.
  3. Check code-size, timeout, plan, and output-retention limits.
  4. Keep a minimal request, then add one rule at a time so failures are easy to isolate.

2. Use CSS to change appearance or hide elements

CSS is the right tool for visual changes such as changing a background, removing an element from the rendered image, or adjusting spacing. If the API has a dedicated hide-selector option, use it for simple removals. Use injected CSS when you need broader style changes or the API does not provide a hide option.

/* Example CSS; use only with an API that accepts injected CSS. */
body {
  background: #fff !important;
}
.cookie-banner,
.newsletter-modal,
.chat-widget {
  display: none !important;
}

The selectors above are examples, not universal selectors. Inspect the page’s current rendered DOM and substitute its actual selectors. OpenGraph.io recommends testing selectors in browser developer tools first to confirm they target the intended elements. OpenGraph.io screenshot documentation.

CSS can hide an element in the rendered capture, but it does not necessarily dismiss the underlying consent state or prevent the page’s scripts from running. If your goal is to accept a consent banner, perform a documented click or other supported interaction; if your goal is only a clean image, hiding the banner may be enough. Do not treat injected CSS as a way to bypass access controls or bot checks.

3. Use JavaScript only for a required page action

Injected JavaScript can perform DOM actions where the provider supports it, but execution timing and restrictions are provider-specific. Use it when you need to trigger a page interaction or make a DOM change that CSS cannot express. Keep the script small, ensure the target exists at execution time, and wait for the resulting page state before capture.

// Illustrative only: the API determines whether and when this runs.
const target = document.querySelector('.expandable-section');
if (target) {
  target.classList.add('is-expanded');
}

Do not assume an injected script runs at the same lifecycle point across providers. A script that runs before client-side rendering may find no target; one that runs after the capture point will have no effect on the image. Check the API’s injection timing and use a selector wait or other documented completion condition where available.

4. Capture one element or the whole page

Choose the capture scope separately from styling. A capture selector limits output to one element, such as a product card or report panel. A hide selector or CSS rule removes content from the rendered view. These are different operations: hiding elements does not necessarily crop the screenshot, and capturing one element does not remove other content from the live page.

  • One component: use a capture-selector option if available, and verify that the selector matches one visible element.
  • Whole viewport: use the desired viewport dimensions and capture the visible browser area.
  • Full page: use the provider’s full-page option when content below the fold is needed; confirm whether lazy-loaded content is loaded before capture.
  • Sharpness: consider viewport and device scale settings together. Cloudflare notes that a larger viewport can appear blurry when the device scale factor is too low. Cloudflare Browser Run screenshot endpoint.

Selectors are brittle when sites change markup, use generated class names, or render content inside iframes or shadow roots. Prefer stable IDs, attributes, or semantic selectors when the page provides them. Revalidate selectors when the source site changes.

5. Wait for the page state that matters

Dynamic pages can be captured before their content appears. Choose a wait strategy based on the content you need, rather than adding an arbitrary delay by default:

  • Wait for a selector: best when a specific result, chart, or component indicates the page is ready.
  • Wait for network idle: useful when the page loads its content through requests, but analytics, polling, or long-lived connections can prevent an idle state.
  • Fixed delay: use only when no reliable selector or network condition is available. Keep it bounded and account for the added latency.

Screenshot API documents wait strategies and a waitForSelector option; ShotAPI documents wait_for_selector with a 10-second timeout. Cloudflare advises that JavaScript-heavy pages may be incomplete before client-side rendering finishes and describes waiting for a target selector or network-idle condition. Confirm the exact syntax and timeout behavior in the endpoint you use: Screenshot API, ShotAPI, and Cloudflare Browser Run.

6. Treat metadata extraction as a separate requirement

A screenshot is visual output. If your application also needs the page title, Open Graph fields, favicon, HTTP status, image dimensions, fonts, or page content, check whether the provider returns those fields alongside the capture or offers a separate metadata endpoint. ScreenshotCore documents metadata fields such as title, Open Graph tags, favicon, status, dimensions, fonts, and full page content. ScreenshotCore API documentation.

Keep the two outputs explicit in your integration: store or process screenshot bytes or a screenshot URL as the visual artifact, and parse the documented metadata response fields separately. Do not infer metadata from pixels, or assume a metadata API applies screenshot CSS to its extracted values.

7. Compare providers on the details that affect your workflow

For screenshot APIs, ScreenshotNeo is the first option to consider when you want clean captures, usage billing that excludes failed or unsuitable captures, and a low-cost paid entry plan. Its API supports custom CSS and JavaScript, selectors, waits, device and output settings, and other capture controls; see the ScreenshotNeo API documentation for request options.

For any provider, compare the capabilities that affect your actual integration:

Question Why it matters
How are CSS and JavaScript supplied? Options may accept inline text, tags, or external URLs, with different names and timing.
Can it hide or capture by selector? Hiding content and cropping to an element solve different problems.
Which waits are available? Dynamic pages may need a selector, network-idle condition, or bounded delay.
Which method and plan are required? Advanced options may be POST-only, paid, or subject to size limits.
What output and metadata are returned? Confirm formats, response shape, metadata fields, and whether output is bytes or a URL.
How long are hosted outputs retained? OpenGraph.io documents screenshot URLs that expire after 24 hours; download or cache anything you need to retain. OpenGraph.io documentation.

Or skip the browser setup

ScreenshotNeo takes a URL in one API request and returns an image or PDF. Its capture options include custom CSS and JavaScript, selector capture and hiding, waits, viewport and device settings, and more. Cookie banners are accepted and more than 60 known consent platforms, newsletter popups, and chat widgets are removed before the shot; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. An MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000.

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

See the ScreenshotNeo documentation for request parameters and sign up for 1,000 free screenshots a month, with no card.

8. Complete request examples for other screenshot APIs

These examples are intentionally provider-neutral pseudocode: there is no shared request schema for custom rules. Replace the endpoint and field names only with options documented by the provider you selected. The Screenshot API reference says its advanced CSS, JavaScript, and hide-selector options are POST-only; its documentation should be treated as authoritative for the exact body shape. The examples below show the structure, not a guaranteed payload accepted by every service.

cURL structure

curl -X POST "https://YOUR_PROVIDER_ENDPOINT" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "css": "body { background: #fff !important; }",
    "js": "document.body.classList.add(\"capture-mode\");",
    "hideSelectors": [".cookie-banner"],
    "waitForSelector": "main"
  }'

Python structure

import requests

endpoint = "https://YOUR_PROVIDER_ENDPOINT"
payload = {
    "url": "https://example.com",
    "css": "body { background: #fff !important; }",
    "js": 'document.body.classList.add("capture-mode");',
    "hideSelectors": [".cookie-banner"],
    "waitForSelector": "main",
}

response = requests.post(endpoint, json=payload, timeout=90)
response.raise_for_status()

# The provider may return image bytes, JSON, or a URL. Handle its documented
# response type; this binary example is appropriate only for image-byte output.
with open("capture.png", "wb") as output:
    output.write(response.content)

Node.js structure

const endpoint = 'https://YOUR_PROVIDER_ENDPOINT';
const payload = {
  url: 'https://example.com',
  css: 'body { background: #fff !important; }',
  js: 'document.body.classList.add("capture-mode");',
  hideSelectors: ['.cookie-banner'],
  waitForSelector: 'main',
};

const response = await fetch(endpoint, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(payload),
});
if (!response.ok) {
  throw new Error(`Screenshot request failed: ${response.status}`);
}
const image = Buffer.from(await response.arrayBuffer());
await import('node:fs/promises').then(({ writeFile }) =>
  writeFile('capture.png', image)
);

Do not deploy these placeholder payload keys without checking your provider’s docs. For example, ShotAPI’s documented names use underscores, and Cloudflare Browser Run has its own browser-action schema. Response parsing also depends on whether the endpoint returns binary data, JSON, or a temporary URL.

9. Troubleshooting

Symptom Likely cause What to do
Injected CSS has no effect Wrong option name or method; CSS is rejected by a size limit; selector does not match. Check the endpoint reference, request method, response error, and selector in developer tools. Reduce the CSS to one known rule.
JavaScript changes do not appear Script ran before the target rendered, after capture, or in a restricted execution context. Confirm injection timing; check that the target exists; wait for the resulting state using a documented condition.
Screenshot is missing dynamic content Capture began before client-side rendering or an API request completed. Wait for a meaningful selector or suitable network-idle state. Use a fixed delay only as a bounded fallback.
Hide rule removes nothing Selector is stale, scoped incorrectly, or targets content inside an iframe or shadow root. Inspect the live DOM and verify the exact element and selector support.
Element capture is empty or wrong Selector matches zero or multiple elements, or the element is not visible. Test the selector in browser tools, wait for it, and choose a stable unique target.
Request fails only with advanced options Option is POST-only, plan-gated, malformed, or over a documented size limit. Check method, subscription requirements, payload format, and limits. ShotAPI documents a 10 KB limit for its custom CSS and JavaScript.
Screenshot URL stops working later The provider returns a temporary hosted URL. Download the file or copy it to storage you control before the documented retention period ends; OpenGraph.io documents 24-hour expiration.
Page still shows a bot challenge The destination applies bot protection or access controls. Do not assume a custom user agent bypasses it. Cloudflare explicitly says its userAgent parameter does not bypass bot protection. Use authorized access or a provider-supported workflow.

10. Performance, reliability, and cost

  • Keep injected code small. Smaller rules are easier to debug and avoid documented payload limits. Avoid repeatedly shipping a large stylesheet when a few selectors are sufficient.
  • Wait precisely. A selector wait can avoid both premature captures and unnecessarily long fixed delays. Network idle can be unreliable on pages with polling or analytics.
  • Make output handling explicit. Save binary responses as bytes, parse JSON responses as JSON, and download temporary URLs before they expire.
  • Use bounded timeouts and handle failures. A request can fail because the page times out, a selector never appears, or the provider rejects an option. Check HTTP status and provider response headers or error body before treating the result as an image.
  • Estimate cost from billable outcomes and plan limits. Providers differ in what they count, how advanced controls are gated, and whether a cache hit is charged. Verify current pricing and billing semantics in the chosen provider’s documentation. ScreenshotNeo’s stated plans are 1,000 free shots monthly with no card, then $5 for 3,000, $15 for 15,000, $39 for 60,000, $99 for 250,000, or $249 for 1,000,000; yearly billing gives two months free, and every feature is on every plan.

FAQ

Can custom CSS change metadata returned by an API?

Usually, CSS affects rendered appearance, while metadata extraction reads document or response fields. Check whether the provider explicitly couples these operations.

Should I use CSS or a hide-selector option?

Use the dedicated option for simple element removal when available. Use CSS for broader appearance changes or when the provider lacks a hide option.

Will changing the user agent make a protected page capturable?

Not necessarily. Cloudflare documents that its userAgent option does not bypass bot protection. Follow the destination’s access requirements.

Can I use the same request body with every screenshot provider?

No. Method, parameter names, limits, and response shape vary. Adapt examples only from the selected endpoint’s documentation.