How to Make Website Thumbnails for URLs with Long Query Strings
Encode the full page URL as one screenshot request parameter, preserve the query values that affect the page, and check the renderer’s input limit.
To make a website thumbnail from a URL with a long query string, pass the complete target URL as one encoded value to your screenshot tool. The target page’s ?, &, and = belong to the inner URL; the screenshot service’s own parameters form a separate, outer query string. Use a URL or parameter API to build that outer request instead of concatenating strings by hand.
Encoding protects the boundary between the two query strings. It does not shorten the target URL or remove the screenshot provider’s input limit. Preserve query parameters that affect the rendered page, check your provider’s documented limit, and keep production credentials on the server.
1. Understand the two URLs
A screenshot request contains an outer service URL and an inner page URL. For example, the target page below has its own query parameters:
https://shop.example/products?color=blue&sort=price&return=%2Foffers%3Fsource%3Demail
If you append that target URL raw to an endpoint, its ampersands can be interpreted as separators between the screenshot service’s parameters. The service may then receive a truncated target URL and extra, unintended parameters. Encode the target URL as the value of the outer url parameter. Google Search Central’s URL guidance says reserved characters must be percent-encoded; see its URL structure best practices.
Prefer a parameter-building API such as JavaScript’s URLSearchParams or Python’s requests params argument. Those tools handle encoding the value for you. Do not percent-encode the same URL twice.
2. Make a thumbnail yourself with Playwright
If you want to render the page in your own browser automation setup, pass the target URL to the browser as a URL, rather than assembling a screenshot endpoint URL. The inner query string is then part of the navigation URL and does not need to be split out or removed.
Python: install and capture
Install Playwright and its Chromium browser once:
python -m pip install playwright
python -m playwright install chromium
Save the following as thumbnail.py. It accepts the full page URL as a command-line argument, validates that it uses HTTP or HTTPS, sets a thumbnail-sized viewport, waits for the page’s load event, and writes a PNG.
import asyncio
import sys
from urllib.parse import urlsplit
from playwright.async_api import async_playwright
async def main():
if len(sys.argv) != 2:
raise SystemExit('Usage: python thumbnail.py <full-page-url>')
target = sys.argv[1]
parsed = urlsplit(target)
if parsed.scheme not in ('http', 'https') or not parsed.netloc:
raise SystemExit('Provide a complete HTTP or HTTPS URL')
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page(
viewport={"width": 1280, "height": 800},
device_scale_factor=1,
)
try:
response = await page.goto(
target,
wait_until='load',
timeout=60_000,
)
if response is not None and response.status >= 400:
raise RuntimeError(f'Target page returned HTTP {response.status}')
# Optional: wait for a page-specific element before the shot.
# await page.locator('main').wait_for(timeout=10_000)
await page.screenshot(
path='thumbnail.png',
type='png',
full_page=False,
)
print(f'Saved thumbnail.png; final URL: {page.url}')
finally:
await browser.close()
asyncio.run(main())
Run it with the target URL quoted so your shell does not treat ampersands as background operators:
python thumbnail.py 'https://shop.example/products?color=blue&sort=price&return=%2Foffers%3Fsource%3Demail'
Playwright navigates to the supplied URL; the query parameters remain part of that navigation. Do not run this command with an untrusted URL in a privileged environment: a URL renderer makes network requests and should be isolated and restricted according to your application’s security needs.
Make the output a smaller thumbnail
The example captures the viewport at 1280 × 800. For a 320 × 200 thumbnail, either set the viewport to those dimensions or capture at a larger size and resize the resulting image. Capturing at a larger viewport can keep page layout closer to a desktop view; resizing changes the final pixel dimensions. For legible small previews, choose a consistent aspect ratio and avoid capturing full pages with very tall content.
Use full_page=True only when you need the entire document. A full-page image can be very tall, larger to transfer, and harder to read when displayed as a thumbnail. Lazy-loaded images may not appear unless the page scrolls or the application waits for them; add page-specific scrolling or readiness logic when needed.
3. Call a screenshot API with a long target URL
For a GET-based screenshot service, pass the target URL as a parameter value through an HTTP client. For example, Screenshot API’s documentation uses cURL’s --data-urlencode for its url parameter and states a 2,048-character maximum for that provider’s target URL. This limit is specific to that service, not a universal URL limit. Check the current limit and supported options for the provider you choose: Screenshot API documentation.
cURL
curl -G 'https://api.screenshot-api.net/v1/screenshot' \
--data-urlencode 'url=https://shop.example/products?color=blue&sort=price&return=%2Foffers%3Fsource%3Demail' \
-o thumbnail.png
Here, cURL encodes the whole url value for the outer request. Add only documented options for the chosen endpoint. Do not assume every screenshot service accepts the same parameter names, output formats, or authentication methods.
Python with requests
import requests
endpoint = 'https://api.screenshot-api.net/v1/screenshot'
target = 'https://shop.example/products?color=blue&sort=price&return=%2Foffers%3Fsource%3Demail'
response = requests.get(
endpoint,
params={'url': target},
timeout=90,
)
response.raise_for_status()
content_type = response.headers.get('Content-Type', '')
if not content_type.startswith('image/'):
raise RuntimeError(f'Expected an image response, received {content_type!r}')
with open('thumbnail.png', 'wb') as image:
image.write(response.content)
If the chosen provider requires authentication or other rendering parameters, add them using that provider’s documented method. Keep secret keys in server-side configuration, not a public web page or client-side script.
Node.js with fetch
const endpoint = new URL('https://api.screenshot-api.net/v1/screenshot');
const target = 'https://shop.example/products?color=blue&sort=price&return=%2Foffers%3Fsource%3Demail';
endpoint.searchParams.set('url', target);
const response = await fetch(endpoint, { signal: AbortSignal.timeout(90_000) });
if (!response.ok) {
throw new Error(`Screenshot request failed: HTTP ${response.status}`);
}
const contentType = response.headers.get('content-type') ?? '';
if (!contentType.startsWith('image/')) {
throw new Error(`Expected an image response, received ${contentType}`);
}
const image = Buffer.from(await response.arrayBuffer());
await import('node:fs/promises').then(({ writeFile }) =>
writeFile('thumbnail.png', image)
);
console.log('Saved thumbnail.png');
Node’s URL and URLSearchParams construct the outer query while preserving the target string as one parameter value. See MDN’s URL documentation and URLSearchParams reference.
4. What to do if the GET request is too long
Percent encoding makes a query parameter safe to parse; it does not guarantee that the final HTTP request fits limits imposed by clients, proxies, servers, or the screenshot service. Encoded characters can make the request longer. If the complete request exceeds a documented limit:
- Check which limit you hit. Compare the target URL length and the complete encoded request with the selected provider’s documented limits. Do not apply one vendor’s limit to another.
- Keep meaningful page parameters. Some parameters control filters, pagination, locale, session state, or the content being rendered. Remove only values you know are unnecessary for the requested page.
- Use POST only if the provider documents it for capture. Some screenshot endpoints accept JSON bodies; others accept only GET. Confirm the endpoint, fields, and authentication in the current API reference before changing methods.
- Consider a path-encoded endpoint if supported. Webshrinker documents a v2 screenshot route that puts a base64-encoded target URL in a path segment. Base64 itself can expand the value, so this does not eliminate every request-length limit. Its documented route and authentication options are in the Webshrinker Website Screenshot API reference.
Do not switch to a path or body format based on guesswork. The service must explicitly support that request design.
5. Preserve the right query parameters
A long query string may include both content-defining values and tracking values. Removing every parameter can change what the screenshot shows. For example, color=blue, page=3, language=fr, or a return path may affect the rendered page. A campaign tag might not. The page application determines the meaning.
- Keep repeated parameters when the destination uses them, such as
tag=a&tag=b. - Preserve already encoded values such as
%2F. Avoid decoding and re-encoding blindly, especially if the destination distinguishes encoded delimiters from actual delimiters. - Fragments (the part after
#) are not sent to the server in an HTTP request, but client-side applications may use them to select content or state after load. - Do not sort or deduplicate parameters unless the destination’s behavior permits it.
- Validate the scheme and host if URLs come from users. Limit which hosts your rendering service can access to reduce server-side request risks.
6. Authentication and public thumbnail URLs
A public image URL can expose anything embedded in its query string to browser source, network logs, referrer data, caches, or people who receive the link. Do not put production API secrets in a public image src or browser-side JavaScript. Screenshot API documents bearer authentication and warns about exposing query-string keys; Webshrinker documents server-side Basic authentication and signed URLs. Use each provider’s current documented credential pattern.
For an application that needs public thumbnails, have your server request the screenshot and store or proxy the resulting image, or use a provider’s documented signed-link feature where appropriate. Treat signed links as access tokens: set an expiry if available and avoid logging or sharing them beyond their intended use.
7. Thumbnail settings, performance, and reliability
| Decision | Practical guidance |
|---|---|
| Viewport | Choose dimensions that match the layout you want to preview. A narrow viewport may trigger a mobile layout; a desktop viewport may better represent a desktop page. |
| Viewport versus full page | Viewport capture is usually a smaller thumbnail. Full-page capture can include more content but may produce a very tall image and take longer. |
| Readiness | Wait for the page state that matters. Network idle can be unreliable on pages with polling or persistent connections; a specific selector or a measured delay can be more suitable when supported. |
| Images and fonts | External assets can load late or fail independently. If the screenshot must contain them, wait for the relevant assets or element before capture. |
| Timeouts and retries | Set a finite request timeout. Retry transient network or server failures with a small bounded backoff; do not retry deterministic invalid URLs or authentication errors unchanged. |
| Cache | Cache by the normalized target URL plus every setting that changes the output, such as viewport, color scheme, or wait behavior. Avoid treating distinct meaningful query values as the same page. |
| Storage and cost | Smaller viewport images generally transfer and store less data than full-page captures. Compare providers using their documented pricing, billing rules, limits, and cache behavior; the dossier verifies no general cost benchmark across providers. |
For reproducible thumbnails, record the final URL after redirects and the capture settings alongside the image. Avoid logging sensitive query values: long URLs may contain session tokens or personal data. If the target uses expiring or signed parameters, retries can fail after the signature expires; generate a fresh target URL when appropriate.
8. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The screenshot shows the wrong page or misses filters | The target URL was truncated at an inner &, or a meaningful parameter was removed. |
Pass the entire URL as one parameter value with a parameter API or --data-urlencode. Compare the actual target received by the renderer with the original. |
| The service reports an invalid URL | The target lacks a scheme, has malformed percent escapes, or was encoded twice. | Pass a complete https:// URL and let one encoding layer handle the outer parameter. Validate the target before making the request. |
| HTTP 414 or a request-too-long error | The encoded request exceeds a limit somewhere along the route. | Check the provider’s target URL and request limits. If supported for that endpoint, use a POST body or an alternate documented route. Encoding alone does not reduce length. |
| The image contains an error page or is blank | The target returned an error, needs authentication, blocks automated browsers, or had not rendered the needed content before capture. | Check the final URL and HTTP status, confirm the page is accessible to the renderer, and wait for a page-specific ready element. Do not assume every service can access private pages. |
| Some query parameters disappear or change | A parser or manual string operation split, normalized, or reconstructed the URL incorrectly. | Use URL/URLSearchParams or a requests library’s parameter argument. Preserve duplicate parameters and encoded values when they matter to the destination. |
| Node receives HTML or JSON instead of an image | The endpoint returned an error body or a job/status response. | Check the HTTP status and Content-Type before saving bytes as an image. Handle asynchronous responses according to that provider’s documentation. |
| Python shell command stops at an ampersand | The URL was not quoted, so the shell treated & as a control operator. |
Quote the complete command-line URL, or pass it as an argument from your application instead of building a shell command. |
| Credentials appear in logs or page source | A production secret was placed in the public request URL. | Rotate exposed credentials and move authentication to a server-side header or other documented secure mechanism. |
9. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. Its API accepts a URL in one GET request and returns an image or PDF. Pass the full target URL as one parameter; the HTTP client encodes it for the outer request. See the ScreenshotNeo API documentation for request options and limits.
cURL
curl -G 'https://api.screenshotneo.com/v1/shot' \
-d 'access_key=YOUR_API_KEY' \
--data-urlencode 'url=https://shop.example/products?color=blue&sort=price&return=%2Foffers%3Fsource%3Demail' \
-o shot.webp
Python
import requests
url = 'https://shop.example/products?color=blue&sort=price&return=%2Foffers%3Fsource%3Demail'
r = requests.get(
'https://api.screenshotneo.com/v1/shot',
params={'access_key': 'YOUR_API_KEY', 'url': url},
timeout=90,
)
r.raise_for_status()
open('shot.webp', 'wb').write(r.content)
Node.js
const target = 'https://shop.example/products?color=blue&sort=price&return=%2Foffers%3Fsource%3Demail';
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: target });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: HTTP ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
Keep the API key server-side. ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.
Sign up free for 1,000 screenshots a month, no card required.
10. FAQ
Does a long query string make a page impossible to screenshot?
No. It can make the screenshot request longer, but the page can still be captured when the renderer accepts the full URL and its request limits are met.
Should I shorten the target URL before capturing it?
Only if you know which values are unnecessary and the shorter URL renders the same page. Removing a filter, locale, or state parameter can change the screenshot.
Does base64 encoding guarantee a shorter request?
No. Base64 can expand data. It changes how a service represents the target URL; it does not guarantee the request will fit every intermediary’s limits.
Can the screenshot include the URL fragment?
The fragment is handled by the browser rather than sent to the server. Client-side pages may still use it after navigation, so keep it if it selects the content to capture.


