I built this actor; it's a paid tool on Apify with a free trial credit.
If you sell baby clothes, bedding or toys in Europe, the first thing a buyer or retailer asks your fabric supplier for is an OEKO-TEX STANDARD 100 certificate, ideally in product class I (articles for babies and toddlers). The OEKO-TEX Buying Guide lists every company that holds one, but it is a search form with one profile page per company, so building a supplier list from it means a lot of clicking.
I'm an 18-year-old engineering student, and I built OEKO-TEX Certified Textile Suppliers Scraper to turn "fabrics, China, STANDARD 100" into one JSON row per certified company. This post covers what the data holds, calling it from Python and Node, and a small project: a shortlist of mills that are certified for baby articles and also hold the STeP production-site label.
One limit up front: the Buying Guide has no prices, no MOQs, no certificate numbers and no validity dates. It tells you who is certified for what. You still have to ask the supplier for the certificate and check the label number yourself.
How it works
The actor reads the Buying Guide at services.oeko-tex.com over plain HTTP, with no browser and no login. You pick a standard (100, leather, step, ecopass, mig or organic-cotton), countries or a region, and optionally a product level, product types, materials, search words and a STANDARD 100 product class. Every combination of country, material, product type and search word becomes its own search with its own row limit.
With includeProfiles on, it opens each company's profile for the street address, website, role mailbox, phone, every label the company holds, and its certified product levels, types, materials, uses and product classes.
Three things worth knowing:
- Country names follow the Buying Guide ("Türkiye", "Korea, South"), but "Turkey", "South Korea", "USA" and "UK" are mapped for you.
- Profile links carry a session token, so they stop working after a while. Search again to refresh them.
- Most profiles list a named contact person. The actor never reads that person, and it keeps only role mailboxes like info@ or sales@. That is why e-mail is empty on most rows.
Calling it from Python
from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("rel8ble/oeko-tex-suppliers-scraper").call(run_input={
"standard": "100",
"countries": ["China"],
"productLevel": "Fabrics",
"includeProfiles": True,
"maxResultsPerSearch": 20,
})
for r in client.dataset(run["defaultDatasetId"]).iterate_items():
print(r["company"][:40], "|", r["city"], "|", ", ".join(r["labels"]), "|", r["productClasses"])
The same from Node, this time for cotton woven fabric in Portugal and Türkiye, keeping only companies with a website or role mailbox:
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('rel8ble/oeko-tex-suppliers-scraper').call({
standard: '100',
countries: ['Portugal', 'Turkey'],
productLevel: 'Fabrics',
materials: ['Cotton'],
requireWebsiteOrEmail: true,
maxResultsPerSearch: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, 'certified cotton fabric makers with a website');
What a row looks like
From the prefilled run (STANDARD 100, Fabrics, China, 7 November 2026), trimmed:
{
"company": "Advance Denim Co., Ltd.",
"country": "China",
"city": "Foshan",
"postalCode": "528306",
"website": null,
"email": null,
"labels": ["STANDARD 100", "STeP", "MADE IN GREEN"],
"productLevels": ["Fabrics"],
"productTypes": ["Woven fabric"],
"materials": ["Cotton", "Elastanes and other elastic fibres", "Polyester"],
"productClasses": ["I"],
"annexFlags": { "Appendix 4": false, "Appendix 6": true, "Recycling": true }
}
In those 20 rows, street, labels, product types and materials were filled for every company and the post code for 17. Only 5 had a website and 1 a role mailbox, which is normal for Chinese mills on the guide. In a Portugal finished-goods test with the website-or-email filter on, 92 of 115 matches were kept and 99% of those had a website.
What the 20 China fabric rows contain:
- Labels: all 20 hold STANDARD 100, 3 also hold STeP, 1 holds MADE IN GREEN.
- Product class: 11 are certified in class I (baby articles), 10 in class II (skin contact), 1 in class IV (decoration). Some hold two.
- Materials: polyester shows up on 16, cotton on 7, polyamide on 5, wool on 2.
- Product types: 9 woven fabric, 8 knitted fabric, plus interlinings, linings, non-wovens and dye houses.
Project: a baby-product fabric shortlist
STANDARD 100 class I is the strictest class, meant for articles for children up to 3. STeP certifies the production site itself (chemicals, environment, working conditions). A mill that holds both is a reasonable first call for a baby brand. Filter for both and write a CSV:
import csv
run = client.actor("rel8ble/oeko-tex-suppliers-scraper").call(run_input={
"standard": "100",
"countries": ["China", "India", "Turkey", "Portugal"],
"productLevel": "Fabrics",
"productClass": "I",
"materials": ["Cotton"],
"includeProfiles": True,
"maxResultsPerSearch": 200,
})
rows = list(client.dataset(run["defaultDatasetId"]).iterate_items())
short = [r for r in rows if "STeP" in r["labels"]]
print(len(rows), "class I cotton fabric makers,", len(short), "also hold STeP")
with open("baby-fabric-shortlist.csv", "w", newline="", encoding="utf-8") as f:
w = csv.writer(f)
w.writerow(["company", "country", "city", "labels", "types", "materials", "website", "email"])
for r in sorted(short, key=lambda r: (r["country"], r["company"])):
w.writerow([r["company"], r["country"], r["city"], "; ".join(r["labels"]),
"; ".join(r["productTypes"]), "; ".join(r["materials"]),
r["website"] or "", r["email"] or ""])
On the 20 China rows from the demo run, that filter leaves 3 companies: Advance Denim in Foshan (woven, also MADE IN GREEN), Black Peony Textile in Changzhou (woven cotton/elastane) and Changzhou Dongheng Printing & Dyeing. That is 3 of 20, so the STeP label cuts a list hard, which is the point.
A row means the company is listed in the Buying Guide for those labels. It is not proof that a given product is covered. Ask for the certificate, check the label number with OEKO-TEX's own label check, and order samples before you commit.
Pricing and limits
$2.00 per 1,000 companies, platform usage included, no start fee. Searches that find nothing, duplicates, companies skipped by the website-or-email filter and failed requests are never charged. Results come in alphabetical order, so a small maxResultsPerSearch gives you companies starting with A to C; set it to 0 for the whole list. If a filter has no valid value at all, the run stops with an input error instead of searching everything.
The Buying Guide is free to browse. You pay for the ready rows, the profile fields and the filters. The actor is not affiliated with OEKO-TEX; the name only describes the data.
Store page: https://apify.com/rel8ble/oeko-tex-suppliers-scraper
This article was drafted with AI and published by me.
Need a custom scraper or a ready dataset? Hire me on Fiverr: https://www.fiverr.com/rel8ble
Top comments (0)