Yupoo Scraper avatar

Yupoo Scraper

Under maintenance

Pricing

from $6.00 / 1,000 product scrapeds

Go to Apify Store
Yupoo Scraper

Yupoo Scraper

Under maintenance

Scrape Yupoo stores, categories, or single albums: images, titles, descriptions, and album URLs.

Pricing

from $6.00 / 1,000 product scrapeds

Rating

0.0

(0)

Developer

Ibrahim

Ibrahim

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

🖼️ Yupoo Scraper — Albums, Images, Titles, Descriptions

Point it at a Yupoo store, category, or single album and get clean structured data back. No login, no browser automation, no guesswork.

This Actor does one job properly: it reads Yupoo pages and hands you exactly what is on them — image URLs, titles, descriptions, album URLs. It does not score, rank, rewrite, or "enrich" anything. What you see in the output is what the seller published.


💡 Who is this for?

  • Catalog and inventory work — pulling a supplier's full album list into a spreadsheet or database.
  • Developers building on top of Yupoo data who want a clean JSON feed instead of writing an HTML parser.
  • Anyone doing bulk image collection from albums that hold 30–100 photos each.

🎯 What it does

You give itYou get back
A store URL — https://STORE.x.yupoo.com/albumsEvery album in the store
A category URL — https://STORE.x.yupoo.com/categories/5280382Every album in that category
A single album URL — https://STORE.x.yupoo.com/albums/246388734?uid=1That one album
A bare store namechaosmade20Same as the store URL

Mix as many as you like in one run — each URL is detected automatically, no mode switch to set.


⚡ Quick start

Just the album URLs from a store (fastest and cheapest — reads index pages only, never opens an album):

{ "urls": ["chaosmade20"], "albumUrlsOnly": true }

Everything from one category:

{ "urls": ["https://chaosmade20.x.yupoo.com/categories/5280382"] }

Only the images from a single album:

{
"urls": ["https://chaosmade20.x.yupoo.com/albums/246388734?uid=1"],
"fields": ["images"]
}

💡 Start small. Run one album first and check the output is shaped the way you need before pointing it at a store with thousands of albums. Yupoo stores vary a lot — some put their whole catalog on one page, some paginate, some leave descriptions empty.


📤 Output

One record per album, pushed straight to the Dataset — no file keys to hunt for, no post-processing step. Export as JSON, CSV, or Excel from the Storage tab, or pull it from the API.

{
"store": "chaosmade20",
"albumUrl": "https://chaosmade20.x.yupoo.com/albums/246388734?uid=1",
"title": "Washed Black Tee",
"description": "https://example.com/products/tee",
"images": [
"https://photo.yupoo.com/chaosmade20/95d571333a/big.jpg",
"https://photo.yupoo.com/chaosmade20/a87080adfd/big.jpg"
]
}

store and albumUrl are always present. Everything else is whatever you selected in fieldsunselected fields are left out entirely, not returned as empty strings or nulls.


⚠️ Read this before you import images anywhere

Yupoo image URLs are hotlink-protected. This is a property of Yupoo, not of this Actor, and it will affect you no matter which scraper you use. Measured directly:

How the image is requestedWhat Yupoo returns
No Referer headerHTTP 567 — a 7 KB HTML error page, not an image
Referer from your own siteHTTP 567 — blocked
Referer: https://STORE.x.yupoo.com/HTTP 200 — the real JPEG (337 KB)

What this means in practice:

  • Pasting an image URL straight into a browser tab shows a broken image. The URL is still correct.
  • Embedding these URLs directly in a storefront or web page will not work.
  • Downloading them does work — send a Referer header of https://STORE.x.yupoo.com/ with the request:
curl -H "Referer: https://chaosmade20.x.yupoo.com/" \
"https://photo.yupoo.com/chaosmade20/95d571333a/big.jpg" -o image.jpg

Once downloaded, host the files wherever you like and the restriction no longer applies.

✅ The Actor already fixes this for you

Rehosting is on by default. Every image is downloaded from Yupoo using the referer that defeats the block, re-uploaded, and returned as a working URL in the images field — links that display anywhere, in any storefront, with no referer tricks. You do not have to configure anything.

To use your own image-host account instead of the built-in one, set imageHostApiKey. To skip rehosting entirely and get raw Yupoo URLs — much faster and lighter — set rehostImages: false.

{
"urls": ["https://chaosmade20.x.yupoo.com/albums/246388734?uid=1"],
"imageHostApiKey": "YOUR_KEY"
}
imageHostGet a key atNotes
freeimagefreeimage.hostDefault. Chevereto API.
imgbbapi.imgbb.comSupports link expiry.
customAny Chevereto-compatible v1 endpoint via uploadEndpoint.

How it behaves:

  • Keys are validated before the run starts. A wrong or blocked key fails in seconds instead of dying three thousand images into a long job.
  • If no key is available at all, the run still completes — you get every album with its original Yupoo URLs, plus a warning in the log. The scrape is never lost because of an image-host problem.
  • A failed upload keeps its original Yupoo URL rather than dropping out of the results. Your record is never left short — but a run can return a mix of rehosted and original URLs, so make sure your import handles both.
  • The run log reports the split — how many images were rehosted and how many kept their originals.
  • imageLinkExpirySeconds: 0 (the default) keeps images forever. Expiry is an ImgBB feature and is ignored by hosts that do not support it.
  • Resolution is preserved, but the file is re-encoded. Measured on freeimage.host: a 1080×1080 source came back 1080×1080, with the JPEG about 18% smaller. You keep full dimensions; you do not get a byte-identical copy. If you need the exact original file, download it yourself with the referer method above.

⚠️ Free image hosts have their own limits and terms. Bulk-uploading tens of thousands of images may get you rate limited or blocked. Uploads go to your own account, so quotas and content rules are between you and that host. If one blocks you, switch imageHost rather than rewriting anything.


💰 What a run costs

There is no charge from this Actor for rehosting — you bring your own free image-host key, and that host bills you nothing on its free tier. The cost that matters is Apify platform usage, which is driven by requests and data transfer. Measured from real runs:

ModeRequestsData moved
albumUrlsOnly: true1 per ~77–120 albums (index pages only)Negligible
rehostImages: false1 per album, plus index pages~50–150 KB per album
Default (rehosting on)1 per album + 2 per image~780 KB per image

The default row is the one to plan around. Each image is downloaded (~340 KB) and re-uploaded as base64, which inflates it by roughly a third (~440 KB). Albums hold 30–100 images, so a single album moves 25–75 MB.

A worked example: 100 albums averaging 50 images each is 5,000 images ≈ 3.9 GB of transfer and 10,000 upload round trips. That is a long, heavy run.

How to keep it cheap:

  • Use albumUrlsOnly when you only need the catalog structure — it is roughly 100× cheaper than a full scrape.
  • Set rehostImages: false for exploratory runs. Turn it back on for the albums you are actually importing.
  • Use maxImagesPerAlbum to cap rehosting if you only need a few photos per product.
  • Set fields to just what you need. It does not reduce requests (all fields come from the same page) but it does shrink your dataset.
  • Always test on one album first and read the run's usage before scaling to a store.

📥 Input reference

FieldTypeDefaultDescription
urlsarrayRequired. Album, category, or store URLs — mix freely. Bare store names work.
fieldsarrayall threeAny of images, title, description.
albumUrlsOnlybooleanfalseList album URLs without opening each album. Dramatically faster.
maxAlbumsPerStoreinteger100Per store or category URL. 0 = no limit.
maxPagesinteger5Album-index pages to walk.
imageSizestringbigsmall, medium, or big. Costs no extra requests — see below.
maxImagesPerAlbuminteger00 = no limit.
albumConcurrencyinteger4Album pages fetched at once.
requestDelayMsinteger250Minimum pause before each request. Raise if you see 429s.
maxRetriesinteger3Retries per failed request.
rehostImagesbooleantrueOn by default. Re-uploads images so the URLs work anywhere. Set false for a faster, lighter run.
imageHoststringfreeimagefreeimage, imgbb, or custom.
imageHostApiKeystringOptional — use your own image-host account. Stored as a secret.
uploadEndpointstringOnly for imageHost: "custom".
imageLinkExpirySecondsinteger00 = never expire. ImgBB only.
imageConcurrencyinteger4Images downloaded and uploaded at once.
proxyConfigurationobjectApify ProxyYupoo is served from China and can be slow or unreachable from some regions.

🔍 Notes from the field

Things learned the hard way, documented so you do not have to rediscover them:

  • Album URLs need their uid query parameter. Strip it and Yupoo replies 200 OK with a body reading "this album is not exist" — not a 404. A naive scraper writes that as a valid empty record. This Actor keeps the parameter and detects the body anyway, so a dead album is reported rather than silently returned.
  • Two gallery layouts are live simultaneously. Older stores render album3__main cards, newer ones album__main. Both are handled.
  • Titles live in three different places depending on layout. When a page forces a fall back to <title>, that value carries a | album | STORE | … suffix, which is stripped before output.
  • Not every store paginates. Some render the entire catalog on page one. Pagination stops as soon as a page yields nothing new instead of walking empty pages.
  • Albums are large. Across the stores tested, albums held roughly 30–100 images each. That is why maxImagesPerAlbum defaults to no limit — a silent cap would quietly discard most of your data.
  • Image size is a URL rewrite. Yupoo serves /small.jpg, /medium.jpg, and /big.jpg from the same path, so choosing big costs nothing extra.
  • Descriptions are freeform. Sellers write whatever they want there — sometimes a product link, sometimes measurements, sometimes nothing. You get the text as-is.

🔌 API integration

Replace YOUR_TOKEN with your token from Apify Console → Settings → API & Integrations.

Small jobs — one call, results returned inline

For a single album or a small category, run synchronously and get the data straight back:

curl -X POST "https://api.apify.com/v2/acts/Vw1EDApmqzgczTA1a/run-sync-get-dataset-items?token=YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"urls":["https://chaosmade20.x.yupoo.com/albums/246388734?uid=1"],"fields":["images"]}'

The response is the array of results. No polling, no second request.

Large jobs — start, poll, fetch

Big stores take a while, so start the run asynchronously:

curl -X POST "https://api.apify.com/v2/acts/Vw1EDApmqzgczTA1a/runs?token=YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"urls":["chaosmade20"],"maxAlbumsPerStore":0}'

Save data.id (the run ID) and data.defaultDatasetId from the response, poll until the status is SUCCEEDED, then download:

$curl "https://api.apify.com/v2/datasets/DATASET_ID/items?token=YOUR_TOKEN&format=json" -o output.json

Full script (Node.js)

Save as scrape.mjs and run with node scrape.mjs.

const ACTOR = 'Vw1EDApmqzgczTA1a';
const TOKEN = 'YOUR_APIFY_TOKEN';
const BASE = 'https://api.apify.com/v2';
const INPUT = {
urls: ['chaosmade20'], // store, category, or album URLs
fields: ['images', 'title', 'description'],
maxAlbumsPerStore: 0, // 0 = no limit
};
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const getJson = async (url) => (await fetch(url)).json();
// 1. Start the run
const start = await (
await fetch(`${BASE}/acts/${ACTOR}/runs?token=${TOKEN}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(INPUT),
})
).json();
const { id: runId, defaultDatasetId: datasetId } = start.data;
console.log('Run:', runId);
// 2. Poll until it finishes
let status = start.data.status;
while (['RUNNING', 'READY'].includes(status)) {
await wait(30_000);
({ data: { status } } = await getJson(`${BASE}/actor-runs/${runId}?token=${TOKEN}`));
console.log('Status:', status);
}
if (status !== 'SUCCEEDED') throw new Error(`Run ended as ${status}`);
// 3. Download the results
const items = await getJson(`${BASE}/datasets/${datasetId}/items?token=${TOKEN}&format=json`);
const { writeFileSync } = await import('node:fs');
writeFileSync('output.json', JSON.stringify(items, null, 2));
console.log(`Saved ${items.length} albums to output.json`);

🧰 What this Actor does not do

Stated plainly so you can judge fit before spending anything:

  • No AI or LLM processing. Prices, SKUs, and sizes are not extracted from description text. Yupoo has no price field — sellers type prices into freeform text, so any extraction is guesswork. Across the stores tested, price text was absent entirely from 1 in 4 of them. You get the raw description and can parse it yourself if your catalog is consistent.
  • No video extraction. Images only.
  • No password-protected albums.

🖥️ Running it locally

$npm install
$npm test

Put your input in storage/key_value_stores/default/INPUT.json, then:

$node src/main.js

Results land in storage/datasets/default/.


⚖️ Responsible use

This Actor reads publicly accessible pages. It does not bypass authentication, rate limits, or access controls, and it ships with a request delay and conservative concurrency — please leave them in place.

You are responsible for what you do with the output. Images and listings on Yupoo are usually someone else's intellectual property. Republishing or selling based on scraped content may carry legal obligations depending on the content and your jurisdiction. Check before you build on it.