Proxy
Managed residential and datacenter proxies, your own proxies, and how browsers fit in.
Every run can use a managed proxy with residential and datacenter IPs. There's no account or password to set up: each run gets its own credentials, and you pay per GB. You can also bring your own proxy URLs, or mix them with the managed proxy.
const proxyConfiguration = await Actor.createProxyConfiguration({ countryCode: 'US' });How the managed proxy works
When a run starts, the platform injects these environment variables into the container:
| Variable | Value |
|---|---|
SCRAPELY_PROXY_HOSTNAME | Proxy host reachable from inside the run |
SCRAPELY_PROXY_PORT | Proxy port (HTTP and SOCKS5 on the same port) |
SCRAPELY_PROXY_USERNAME | scrapely |
SCRAPELY_PROXY_PASSWORD | The run ID |
SCRAPELY_PROXY_STATUS_URL | Status endpoint used by the SDK access check |
The same values are also set as APIFY_PROXY_* so existing proxy code works unchanged.
The password only works from inside the run that owns it, so Actors never get a reusable proxy credential and there's no account-level password to leak. You can't use the managed proxy outside a run. The SDKs read these variables for you.
Groups and countries
You pick a pool with groups and a location with countryCode:
| Group | Pool | Price |
|---|---|---|
RESIDENTIAL (default) | Residential IPs | $2–$4 per GB |
DATACENTER | Datacenter IPs | $0.75–$1 per GB |
- These two are the only groups. No group means the residential pool.
- The legacy datacenter group names
SHADERandBUYPROXIES94952are accepted and use the datacenter pool. - Any other group, including groups Scrape.ly doesn't offer, such as
GOOGLE_SERP, is refused. It is never routed through another pool or billed. See Unsupported groups. countryCodetakes an ISO 3166-1 alpha-2 code such asUSorDE. The SDKs also acceptsubdivisionCode(needscountryCode), but the proxy targets the country only.- Traffic is billed per GB as run usage and counts toward the run's
maxTotalChargeUsd. If you can't cover it, the run is aborted. - Traffic to your own proxies is never billed as proxy usage.
Unsupported groups
If you ask for a group that doesn't exist, the SDK's access check fails when you create the proxy configuration, and the proxy refuses every request that names it. The message is the same everywhere:
Proxy group "GOOGLE_SERP" is not available on Scrapely. Available groups: RESIDENTIAL, DATACENTER.| Where | What you see |
|---|---|
| Python SDK | Actor.create_proxy_configuration() raises ConnectionError with the message |
| JavaScript SDK | On the platform, Actor.createProxyConfiguration() throws an Error with the message. In local runs it logs the message as a warning |
HTTP proxy, CONNECT or plain request | 407 Proxy Authentication Required, with the message in the X-Scrapely-Proxy-Error header and the response body |
| SOCKS5 | Authentication fails |
This also applies to groups that come from an input field (scrapelyProxyGroups), and to a known group combined with an unknown one (groups: ['RESIDENTIAL', 'GOOGLE_SERP']). Several unknown groups are named together: Proxy groups "GOOGLE_SERP", "UNBLOCKER" are not available on Scrapely. ....
To fall back to a pool that exists, catch the error and create the configuration again with RESIDENTIAL or DATACENTER. Actors built with an older SDK only log a warning at start; their requests then get the 407 above.
Use it from code
On the platform, Actor.createProxyConfiguration() / Actor.create_proxy_configuration() with no options gives you the residential pool:
import { Actor, CheerioCrawler } from 'scrapely';
await Actor.main(async () => {
const proxyConfiguration = await Actor.createProxyConfiguration({
groups: ['DATACENTER'],
countryCode: 'US',
});
const crawler = new CheerioCrawler({
proxyConfiguration,
requestHandler: async ({ request, $ }) => {
await Actor.pushData({ url: request.url, title: $('title').text() });
},
});
await crawler.run(['https://example.com']);
});To use your own proxies, pass proxyUrls / proxy_urls:
const proxyConfiguration = await Actor.createProxyConfiguration({
proxyUrls: [
'http://user:password@proxy.example:8000',
'http://user:password@proxy-2.example:8000',
],
});
const proxyUrl = await proxyConfiguration.newUrl();Rules the SDKs enforce:
proxyUrlscan't be combined withgroupsorcountryCode; the SDK throws.'auto'inproxyUrls(ortieredProxyUrls) reserves a slot for the managed proxy next to your own URLs.newUrl(sessionId)keeps related requests on one IP. Session IDs are at most 50 characters froma-z,A-Z,0-9,.,_, and~.checkAccess(defaulttrue) runs a quick status check at start. If the proxy refuses the configuration, for example an unsupported group, the SDK throws. If the check can't reach the proxy, it logs a warning and the run continues.- Outside the platform, with no
proxyUrls, the SDK warns and returnsundefined.
| SDK | Options | Guide |
|---|---|---|
| JavaScript | Also accepts the input-field keys useScrapelyProxy, scrapelyProxyGroups, scrapelyProxyCountry, and scrapelyProxySubdivision, and their Apify aliases | Proxy & events |
| Python | groups, country_code, subdivision_code, proxy_urls, new_url_function, tiered_proxy_urls, tiered_proxy_config, password, check_access, actor_proxy_input | Environment & proxy |
Let users choose in the input
Add a field with the proxy editor to your input schema:
{
"proxyConfiguration": {
"title": "Proxy configuration",
"type": "object",
"editor": "proxy",
"description": "Use the scrape.ly proxy or your own proxy URLs",
"default": { "useScrapelyProxy": true }
}
}In the dashboard, this field renders a Use Scrapely Proxy (recommended) switch. When the switch is off, users can enter a list of custom proxy URLs. The value looks like {"useScrapelyProxy": true} or {"useScrapelyProxy": false, "proxyUrls": ["http://..."]}, and it can also carry scrapelyProxyGroups, scrapelyProxyCountry, and scrapelyProxySubdivision.
useScrapelyProxy decides whether the run uses Scrape.ly Proxy:
true: requests go through Scrape.ly Proxy, with the groups and country from the same object.falsewithproxyUrls: requests go through your own proxy URLs.falsewithoutproxyUrls: no proxy.Actor.createProxyConfiguration()returnsundefined(Nonein Python).
To pick the pool or the exit country, add them to the same object, for example as the field's default:
{ "useScrapelyProxy": true, "scrapelyProxyGroups": ["DATACENTER"], "scrapelyProxyCountry": "US" }| Key | Meaning |
|---|---|
scrapelyProxyGroups | ["RESIDENTIAL"] (default pool) or ["DATACENTER"]. Other groups are refused |
scrapelyProxyCountry | Two-letter ISO country code, e.g. "US" |
scrapelyProxySubdivision | ISO 3166-2 subdivision, requires scrapelyProxyCountry. The proxy currently routes by country only |
Without scrapelyProxyGroups, the run uses the residential pool.
Apify names
Every key also has an Apify alias, so inputs, tasks and actors written for Apify keep working:
| Scrape.ly name | Apify alias |
|---|---|
useScrapelyProxy | useApifyProxy |
scrapelyProxyGroups | apifyProxyGroups |
scrapelyProxyCountry | apifyProxyCountry |
scrapelyProxySubdivision | apifyProxySubdivision |
Callers and input schemas can use either name. The run receives each proxy object under both names with the same values, whichever naming the caller used. So actor code can read input.proxyConfiguration.useScrapelyProxy or useApifyProxy, and both reflect the user's choice. When an object sets both names, the Scrape.ly name wins. The stored input, the run's Input tab and the API return the input exactly as it was sent.
One exception: an actor that installs the upstream apify JavaScript package itself (no Scrape.ly SDK swap) receives only the Apify names. Versions of that package up to 3.7.0 reject unknown proxy keys.
Pass the value straight to the SDK:
const input = await Actor.getInput();
const proxyConfiguration = await Actor.createProxyConfiguration(input?.proxyConfiguration);Browser crawlers
How browser traffic is proxied depends on the Actor's Use Scrapely browser setting:
| Setting | Where browsers run | Proxy for browser traffic | Billed as |
|---|---|---|---|
| Off (default) | In the run container | The proxyConfiguration you pass to the crawler, as for HTTP crawlers | Proxy GB |
| On | On the Scrape.ly browser | The Scrape.ly browser's own residential proxy. Your proxy URLs are ignored, with a warning | Browser credits |
With the setting on, pick the browser's exit location with a browser configuration rather than a proxy:
const browserConfiguration = await Actor.createBrowserConfiguration({ countryCode: 'DE' });
const crawler = new PlaywrightCrawler({ browserConfiguration, requestHandler });Without one, the browser uses the country of a platform ProxyConfiguration on the crawler, then the run default (US). All options, including state, city, os, and Cloudflare solving: Choose the location and fingerprint.
What's next
- Security: keep proxy credentials out of Git
- JavaScript SDK: proxy and events
- Python SDK: environment
- Publish and monetize: how proxy usage counts toward a user's budget