Guide
How to create many browser profiles, each with its own proxy.
On this page
One account per profile and one exit IP per account keeps accounts apart. With 50 accounts, that means 50 profiles and 50 proxies. Donut Browser imports a proxy list in one paste, makes up to 100 profiles per batch from a template, and pairs profiles with proxies in order. You need Donut Browser installed, the Wayfern engine downloaded, and a list of proxies from your provider.
Limits to plan for
Every new profile gets a new device from the Wayfern engine, and new devices have an hourly limit (a rolling 60 minutes):
| Plan | New profiles an hour | Past the limit |
|---|---|---|
| Free | 8 below 40 profiles, 6 below 80, 4 below 120, then 2 | A warning for one profile. A batch stops at the limit. Going on can block new devices for 24 hours or more. |
| Solo | 10 | Refused until the hour has room |
| Pro, Team, Enterprise | 100 | Refused until the hour has room |
A batch holds 1 to 100 profiles. Local profiles are unlimited on every plan.
1. Import the proxies
- Open Network (Cmd/Ctrl+Shift+N) → Proxies and click Import.
- Drop a
.txtor.jsonfile, or paste the list with Cmd/Ctrl+V. - Set Name Prefix. With the prefix
Shop, the proxies are named “Shop Proxy 1”, “Shop Proxy 2” and so on, after the numbers already in use. - Check the list under “Proxies to import” and click Import N Proxies.
A text list has one proxy per line. These forms work:
socks5://user:[email protected]:1080
http://user:[email protected]:8080
us3.example.com:8080:user:pass
user:pass:us4.example.com:8080
user:[email protected]:8080
us6.example.com:8080A line without a scheme is read as HTTP. Lines that start with # are skipped. When a line of four parts can be read both as host:port:username:password and as username:password:host:port, Donut asks you to pick the right one before the import.
Then select the new proxies and click Check selected. Donut checks 4 at a time and reports “Checks done. Passed: N. Failed: N.” Delete or replace the failed ones now: a profile cannot be created with a proxy that does not work.
2. Save a template
Set up one profile the way every profile in the batch should be: group, tags, status, extension group, DNS blocklist, start pages, operating system and WebRTC mode. Leave its proxy empty, because a template keeps the profile’s proxy and gives it to every new profile. Then open its panel and choose Save as template. New profiles made from the template get its settings and their own new device. The name, the note and the device stay with the original profile.
You can skip this step and set the options in Create new profile directly.
3. Create the profiles
- On the Profiles page, click New (Cmd/Ctrl+N).
- In Create new profile, choose the Template.
- Enter a name, for example
Shop, and set Number of profiles. The dialog shows how many more profiles the hour allows. - Leave the proxy picker on “No proxy / VPN” when each profile needs its own proxy. A proxy chosen here goes to every profile in the batch.
- Click Create. Donut shows “Creating N of M” and a Stop button.
The profiles are named “Shop 1”, “Shop 2” and so on, after the numbers already in use. If you stop the batch, or it reaches the hourly limit, the profiles made so far stay.
4. Give each profile its own proxy
- Select the new profiles in the table.
- In the action bar, open More actions → Distribute proxies.
- In the dialog, tick the profiles and the proxies. The first profile gets the first proxy, the second gets the second, and so on. There is no wrap-around: extra profiles or proxies stay as they are.
- Click Distribute. The toast reads “Assigned a proxy to N profile(s)”.
By default, a proxy that another profile already uses is held back. Turn on Allow sharing a proxy only if you mean to put two profiles on one IP. Running profiles are not changed.
5. Match each profile to its proxy
These profiles were made without a proxy, so their timezone and language come from your own connection. At the first launch, Donut compares them with the new proxy and shows Launch blocked. Click Match fingerprint to proxy, then launch again. See How to match a browser’s timezone and language to its proxy.
The API route below avoids this step, because each profile is made through its own proxy.
Do it all with the API
The local API does the same in three calls, on every plan. A template’s proxy_ids list spreads the proxies over the new profiles, one each in turn, and each profile gets its location from its own proxy. Export the token as DONUT_API_KEY first.
API=http://127.0.0.1:10108/v1
AUTH="Authorization: Bearer $DONUT_API_KEY"
# 1. Import the proxies and keep their IDs.
PROXY_IDS=$(jq -n --rawfile list proxies.txt '{format: "txt", content: $list, name_prefix: "Shop"}' |
curl -sS -X POST "$API/proxies/import" -H "$AUTH" -H "Content-Type: application/json" --data-binary @- |
jq '[.proxies[].id]')
# 2. A template that spreads them.
TEMPLATE_ID=$(jq -n --argjson ids "$PROXY_IDS" \
'{name: "Shop accounts", settings: {name_prefix: "Shop", tags: ["shop"], proxy_ids: $ids}}' |
curl -sS -X POST "$API/templates" -H "$AUTH" -H "Content-Type: application/json" --data-binary @- |
jq -r '.id')
# 3. One profile per proxy.
COUNT=$(echo "$PROXY_IDS" | jq length)
curl -sS -X POST "$API/templates/$TEMPLATE_ID/profiles" -H "$AUTH" \
-H "Content-Type: application/json" -d "{\"count\": $COUNT}" | jq '{made: (.profiles | length), error}'import os, pathlib, requests
API = "http://127.0.0.1:10108/v1"
s = requests.Session()
s.headers["Authorization"] = f"Bearer {os.environ['DONUT_API_KEY']}"
imported = s.post(f"{API}/proxies/import", json={
"format": "txt",
"content": pathlib.Path("proxies.txt").read_text(),
"name_prefix": "Shop",
}, timeout=30).json()
proxy_ids = [p["id"] for p in imported["proxies"]]
print(imported["imported_count"], "imported", imported["errors"])
template = s.post(f"{API}/templates", json={
"name": "Shop accounts",
"settings": {"name_prefix": "Shop", "tags": ["shop"], "proxy_ids": proxy_ids},
}, timeout=30).json()
result = s.post(f"{API}/templates/{template['id']}/profiles",
json={"count": min(len(proxy_ids), 100)}, timeout=600)
body = result.json()
print(result.status_code, len(body.get("profiles", [])), "made", body.get("error"))Creating a profile takes time, so give the last call a long timeout. Donut checks each proxy once before the batch starts.
| Route | Body | Returns |
|---|---|---|
POST /v1/proxies/import | format (txt or json), content, name_prefix | imported_count, skipped_count, errors, proxies |
POST /v1/templates | name (1 to 60 characters, unique), settings | The template with its id |
POST /v1/templates/{id}/profiles | count (1 to 100), optional name | profiles made so far, and error when the batch stopped early |
POST /v1/profiles/distribute-proxies | pairs: a list of profile_id and proxy_id | One result per pair, with ok and error |
See Templates in the API reference.
Troubleshooting
| Error | Status | What to do |
|---|---|---|
PROFILE_GENERATION_LIMIT_REACHED | 429 when nothing was made, or in error with status 200 | The hour is used up. params.retryAfterSeconds says how long to wait. There is no Retry-After header. |
WAYFERN_GENERATION_LIMIT_REACHED: “Temporary fingerprint generation limit reached. Try again later.” | 429 | The engine refused new devices. Wait before the next batch. |
BULK_CREATE_COUNT_INVALID | 400 | count is outside 1 to 100. |
BULK_CREATE_BUSY | 409 | Another batch is running. Wait for it, or stop it in the app. |
BROWSER_NOT_DOWNLOADED | 400 | Download Wayfern in the app first. |
| A proxy or VPN that does not work | 400 | Check the proxies and remove the failed ones. |
In the app, a stopped batch reads “Created N of M profiles.” followed by the reason.
Related
- Profiles and fingerprints: templates, clones and bulk edits.
- How to run several browser profiles at once with Playwright
- How to use a SOCKS5 proxy with a username and password
- How to rotate a proxy’s IP with a change-IP link
Pro raises the limit to 100 new profiles an hour. Compare the plans.
Last updated .