Scrape ouedkniss.com, Algeria's largest classifieds marketplace. Five modes cover every common workflow: search by keyword, browse a category, walk the full category menu, fetch a hand-picked list of listing URLs, or pull the sponsored-listing banner the site shows at the top of every page.
The seller-info row in Seller listings mode. Runs previously emitted one extra row describing the seller itself (identified by attributes.__kind === "seller", carrying the store's public contact block when includeContactInfo: true). Seller listings now returns listing rows only — every row is one of the seller's listings. This also removes the dataset's Seller profile view and the __kind system key.
Changed
Include real phone + email is effective in Specific listing URLs mode only.Seller listings used to attach a store's public contact block to the seller-info row; without that row there is no surface for it, so the toggle now does nothing in seller mode (and, as everywhere else, prints the no-op warning at startup).
Migration
Saved datasets keep their existing rows. New Seller listings runs produce listing rows only — delete the previous attributes.__kind = "seller" row if you already captured one. Re-push the Actor to pick up the new output schema.
[2.0] - 2026-09-04
A ground-up redesign of the input form around one rule: every reference field takes a full page URL, pasted straight from the browser's address bar. No bare IDs, no bare slugs, no mode-specific single-URL fields. The Console form now groups secondary inputs into collapsible sections (Filters / Limits & performance / Output & debugging) instead of one long list.
Removed
Same seller's listings mode (startMode: "sameAuthor") and its Listing ID or URL input. To scrape everything a seller has published, use Seller listings mode and paste the seller's page URL — reachable in one click from any listing row via sellerProfileUrl.
The separate single-listing Listing ID or URL input (similarListingId). Similar products mode now reads the shared Listing URLs input instead.
Added
Listing URLs is now the single input for every mode that starts from a listing page.Specific listing URLs scrapes each pasted URL with full detail data; Similar products fetches the products similar to each pasted URL. Both accept one or more URLs, and both fail with an error naming every invalid entry instead of silently skipping them.
Similar products provenance. With several source listings in one run, each similar row records its source in attributes.similarToId, so results stay easy to group.
Collapsible input sections.Filters, Limits & performance, and Output & debugging group the secondary inputs so the form opens with just the mode picker and its related fields.
Changed
Renamed inputs.Category (categorySlug) → Category page URL (categoryUrl); Seller (sellerId) → Seller page URL (sellerUrl). Both now accept full https://www.ouedkniss.com/… URLs only — category URLs anywhere on the site work (a leading /en/ or /ar/ locale prefix is fine), and seller URLs must be /store/{id}/… or /membre/{id} pages.
URL-only enforcement. Bare values are rejected with a validation error instead of being auto-detected: no bare numeric seller IDs (store and member ID spaces overlap, so only the URL form is unambiguous), no bare category slugs, no bare listing IDs in Listing URLs. Paste the URL from the address bar; that always works.
Empty store grids no longer fall back to a member-profile lookup. A /store/… URL that matches zero listings now returns an empty dataset. The previous fallback probed the numeric ID as a member profile, which could silently scrape an unrelated seller when the store URL was dead.
Seller-info row timing for store runs. The store's seller-info row is emitted when the store grid returns at least one listing (unchanged in practice — a store with listings always produces it).
Migration
Saved tasks that pass sameAuthorListingId, similarListingId, sellerId, or categorySlug will see a schema-validation warning. Re-pick the mode and paste full URLs: seller page URLs into Seller page URL, listing page URLs into Listing URLs, category page URLs into Category page URL. Old bare-ID values cannot be converted automatically — open the corresponding page on ouedkniss.com and copy the URL from the address bar.
[1.7] - 2026-09-02
Added
Similar products mode (startMode: "similar"). Fetches products algorithmically similar to a given listing via OuedKniss's Similar GraphQL query. Paste a numeric listing ID (e.g. 57354225) or a full detail URL into the new Listing ID or URL input — the Actor extracts the ID automatically. Results are flat card projections tagged with attributes.slot = "similar" so consumers can distinguish them from other modes. The query returns up to 30 results in a single response (no pagination).
Same seller's listings mode (startMode: "sameAuthor"). Fetches all listings from a specific seller — paste a listing ID or detail URL into the new Listing ID or URL input and the Actor resolves the seller internally (fetches the listing's detail to extract the user ID, then queries all their listings). This mirrors the website UX where "same seller" listings appear on a product's detail page. Results are paginated (48/page) and tagged with attributes.slot = "sameAuthor". This is the same data as Seller listings mode but accessed through a listing reference rather than a store/profile URL — useful when you found a listing via search and want to browse the seller's other items.
[1.6] - 2026-08-30
Added
Seller listings mode (startMode: "seller"). Scrapes every listing a specific seller has published — the same mode covers business stores (/store/{id}/{slug}/ pages) and personal accounts (/membre/{id} pages), because from a consumer's point of view both are just "a seller". Paste the seller page URL straight from the address bar into the new Seller input and the Actor detects which kind of seller it is from the path; a bare numeric ID (e.g. 290) also works and is tried as a store first, then as a member profile (store and user ID spaces overlap on this site, so the URL form is the unambiguous one). Store grids paginate through the same GraphQL search the storefront uses (filter.storeId, 48/page, shared filter inputs work); member grids run through the site's profile query (24/page). Pagination, maxItems, and maxPages work exactly as in search/category mode. Store-grid prices come back with the priceUnit value UNIT (price already in DZD); the price resolver has always treated unknown units as ×1, so priceAmount is correct.
sellerMemberSince output field. When the seller account was created, ISO-8601 (assumed UTC — the site returns an unzoned YYYY-MM-DD HH:MM:SS). Populated on seller-mode listing rows for member accounts and on the member seller-info row; null everywhere else. This closes the gap noted when the field was dropped in 0.5 — the value simply wasn't exposed by any operation the Actor used back then; the profile query returns it.
Seller-info row. Every seller run now emits one extra row (identified by attributes.__kind === "seller", same partial-row convention as category-tree rows) describing the seller itself: display name, username/ID, profile URL, and for member accounts the follower and announcement counts. A new Seller profile dataset view shows these rows. When the seller is a store and includeContactInfo: true, the row also carries the store's public contact block — phones (with WhatsApp/Viber/Telegram flags), emails, social links, and address — exactly what the storefront page shows to every visitor. Unlike individual sellers' contact info, this is not gated behind the site's click-to-reveal endpoints; the toggle is honored as the user's "no contact data" preference.
Pagination tail guard for store grids. The storefront paginator's lastPage can overshoot the real tail (observed lastPage: 39 where data ended at page 20). The adapter now treats an empty page as "past the end" instead of chasing phantom pages.
Changed
includeContactInfo is now effective in seller mode in addition to directUrls (see the seller-info row above — store contact block only; member profiles expose no contact info on the web). The startup warning no longer fires for seller runs; its text now names the two modes where the toggle does something.
Input schema descriptions for maxItems, maxPages, categorySlug, and the contact-related dataset fields now name the new mode where relevant.
Notes
No dataset-shape migration. Existing rows are unchanged except the new sellerMemberSince field (null everywhere it was previously absent). Seller-mode listing rows use the same organic-row shape as search/category, plus the optional seller-info row. Consumers filtering datasets by populated fields are unaffected.
Member profiles expose no contact info anywhere on the web — the site directs buyers to its mobile app instead. The phone/email arrays for member seller rows and member listing rows are therefore always empty; only stores have a public contact block.
[1.5] - 2026-08-29
Added
Output schema (output_schema.json). The Actor now declares an explicit output schema so the Store/Console Output tab shows the dataset under a "Listings" heading with a description of the per-mode row shape and the three view choices, instead of the default "Output schema is not defined" placeholder. Required for Apify Store publication. The Actor writes only to the default dataset — no key-value store or web-server surfaces — so those schemas are intentionally not defined.
[1.4] - 2026-08-29
Removed
filterConnected input field. The site-side filter this mapped to accepts a Boolean but produces no change in results — paginatorInfo.lastPage and row shape are byte-identical for true, false, null, and omitted values. The input was misleading: it accepted a value, did nothing, and was previously described as a "Site-specific flag exposed by the API. Leave unset unless you know you need it." Dropped from the input schema, the engine's ActorInput type, the adapter's filter builder, and the per-Actor main entry. Saved tasks that pass filterConnected will see a schema-validation warning; remove the field from the task input.
Changed
filterOrigin is now a 3-option select. The site's SearchFilterInput.origin is a strict enum that accepts only INDIVIDUAL (personal sellers) or STORE (business / shops) and rejects any other string with VALIDATION_INVALID_TYPE_VARIABLE. The previous free-text textfield accepted any string and surfaced the server-side error for any value that wasn't one of those two enums. The UI now offers Any (default — omits the field, server returns both), Individual sellers only, or Stores only. The adapter forwards the raw enum value and lets the server be the source of truth.
defaultMemoryMbytes dropped from 256 to 128. Observed peak across recent runs is 110 MB. 128 MB leaves ~16% headroom over the observed peak while halving the per-run compute-unit cost (the workload is single-threaded HTTP I/O, so the smaller CPU share is enough). The min: 128 floor and max: 2048 ceiling are unchanged for users who run huge directUrls lists or want extra headroom.
Notes
Both changes are breaking for any saved task or API caller that passes the old field name. A saved task with filterConnected: true fails input validation with an "Unknown property" warning; a caller that passed a free-text filterOrigin value other than INDIVIDUAL / STORE still hits the same VALIDATION_INVALID_TYPE_VARIABLE they always did, but the new UI prevents it. The Actor is not yet published to the public Apify Store (still dev-only), so this is a clean cut.
[1.3] - 2026-08-29
Added
Category field accepts full category-page URLs. Users used to copy-pasting from the address bar can now paste https://www.ouedkniss.com/automobiles_vehicules/1 straight into the Category field; the adapter's new normalizeCategorySlug helper extracts the slug from the first path segment. Bare slugs (automobiles_vehicules) still work unchanged. URLs that point to non-category surfaces (store pages, member profiles, search results, the homepage, auth pages, etc.) are rejected up front with an actionable error message so we fail loud instead of silently returning an empty banner. Verified against https://www.ouedkniss.com/automobiles_vehicules/1, https://www.ouedkniss.com/immobilier, the bare-domain form www.ouedkniss.com/voitures/2, query strings, fragments, and nested URLs like /automobiles_vehicules/voitures. 11 vitest cases cover the parser (packages/sites/tests/ouedkniss.test.ts).
Two new dataset views in dataset_schema.json:
Category menu — for rows from Category menu (full tree) mode (the What to scrape value "categories"). Shows the category name, slug, parent breadcrumb, depth, rank, icon, and delivery flag. Previously, categories runs left the dataset's Overview view showing only id and scrapedAt for every row because all the other columns were null — users had to switch to the All fields view to see anything meaningful.
Listing details — for rows from Specific listing URLs mode (the What to scrape value "directUrls"). Adds description, seller info, gallery count, video URL, contact info, and the specs map. The existing Listings view (formerly called Overview) stays the default card projection for organic and sponsored-listing rows.
Changed
Renamed every user-facing label to match the actual OuedKniss site vocabulary. The previous labels were either generic ("Keyword search"), developer jargon ("TopAnnounList banner (paid placements)"), or used internals (the enum keys themselves) that don't map to anything a user browsing ouedkniss.com would recognize. The new labels are what the site itself shows:
startMode enum titles: Keyword search → Search by keyword · Category browse → Browse a category · Direct listing URLs → Specific listing URLs · Category tree (recursive) → Category menu (full tree) · TopAnnounList banner (paid placements) → Sponsored listings (banner). "Sponsorisées" / "Publicité" is the actual term OuedKniss uses for the paid banner, so the new label keeps that connection.
startMode title (was Start mode): What do you want to scrape?
searchQuery title: Search keyword. categorySlug title: Category (the word "slug" is meaningless to a non-developer). startUrls title: Listing URLs. locale title: Language, with enumTitles for the three supported values.
maxItems title: Maximum results. maxPages title: Maximum pages. maxDepth title: How deep to walk the menu. maxRequestsPerMinute title: Requests per minute.
Filter titles: Filter: region (wilaya) IDs → Filter by wilaya (region) · Filter: city (commune) IDs → Filter by commune (city) · Filter: min price (DZD) → Minimum price (DZD) · Filter: max price (DZD) → Maximum price (DZD) · Filter: must have pictures → Only listings with photos · Filter: must have a price set → Only listings with a price · Filter: exchange accepted → Only listings that accept trade-in · Filter: delivery available → Only listings with delivery · Filter: connected (OuedKniss-specific flag) → Only 'Connected' listings (OuedKniss feature) · Filter: order by → Sort by with enumTitles for the two orderings · Filter: page size → Items per page.
Overview dataset view renamed to Listings and its description rewritten to point users at the Category menu view for category-tree runs (which now have a meaningful projection) and at the Listing details view for direct-URL runs that want the full enrichment.
actor.json description mentions "sponsored-listing banner" instead of "paid cross-listing banner" so the storefront copy aligns with the new enum title.
README prose uses the new labels throughout (mode table, input tables, field-by-field notes, Notes-and-limits section, FAQ). The Tutorial has a sentence about switching to the Category menu dataset view after a Category menu run, and a sentence about switching to the Listing details dataset view after a Specific listing URLs run with contact info on.
How deep to walk the menu default dropped from 5 to 2. Recursive category-tree walks grow quickly — at depth 5 over OuedKniss's full menu the Actor pushes hundreds of nodes and many API calls, while most users only need the top 2-3 levels to discover a category slug. New default: 2. Raise it explicitly if you need to walk deeper; the field's maximum: 10 is unchanged. The engine-side fallback (packages/engine/src/engine.ts) and the per-Actor main entry (actors/ouedkniss/src/main.ts) were updated to match.
Notes
No dataset-shape migration path. All five modes produce the same Listing row shape they did before — this is purely a UX change (labels + dataset view configuration). Consumers reading the JSON output are unaffected. The dataset view config changes from "Overview" only to "Listings / Category menu / Listing details"; users who script against a specific view name would need to update, but the previous Overview view's transformation.fields and display.properties were preserved under the new Listings name so the columns are byte-identical.
URL acceptance is opt-in via copy-paste. The Category field's UI is still a plain text input, not a URL picker — the helper just makes pasting a URL into it work. Users who want to keep using bare slugs don't need to change anything.
[1.2] - 2026-08-28
Fixed
includeContactInfo was silently ignored outside directUrls mode. The toggle only takes effect in the op.op === "detail" branch of nextOps, which is only seeded by startMode: "directUrls". In search, category, categories, and topAnnounList modes the phone and email arrays were always empty regardless of the toggle — but the old docs only flagged topAnnounList, so users running search/category had no way to know the toggle was a no-op without reading the adapter code. The descriptions for the input field, the two seller-contact output fields, the marketing bullet, the input table, the field-by-field notes, the Notes-and-limits section, and the engine's ActorInput JSDoc now all say explicitly: only effective in directUrls mode. The Actor also prints a startup WARN line when the toggle is enabled outside directUrls, so a user who copy-pastes a config from a previous directUrls run into a search/category run sees the mismatch immediately instead of debugging empty phone arrays after the run completes.
[1.1] - 2026-08-28
Removed
slot field. Every row from a single run has the same slot value (one run = one mode), so the field was constant noise. The discriminator moved out of the data — there's only one Listing shape now; which fields are populated tells you which mode produced the row (organic rows have priceAmount/categoryPath/sellerId, banner rows have storeId/likeCount, category-tree rows have categoryName/categorySlug/parentTreeNames).
status field. Every row the API returns maps to "active" (the resolver returns 'unknown' only when the API sends an unrecognized enum, which doesn't happen in practice). Kept in the engine type only for adapter-internal use; not surfaced in the dataset.
categories view in the dataset schema. Filterable rows for category-tree data are still in the dataset (under the same shape as organic/banner rows), but the dedicated view was redundant with the Overview — Overview is now a complete view of every row, and a filter on categorySlug !== null (in the All fields view) is what splits out tree rows.
ListingStatus and ListingSlot types from @dz/engine. Internal-only after the slot/status removal; no consumer needed them.
Changed
categoryLeafId renamed to categoryId. The previous name's "Leaf" qualifier was confusing — the field holds the listing's category ID, period. categoryId is what consumers see on organic rows; the same value appears as id on rows produced by startMode: "categories", so the rename makes cross-dataset joins obvious.
Breadcrumb arrays → breadcrumb strings.categoryPath (organic row's full path root-to-leaf) and parentTreeIds / parentTreeNames / parentTreeSlugs (category-tree row's ancestors) are now single strings with the parts joined by › (U+203A) — the most common breadcrumb symbol across the web. Empty strings represent "no path" (topAnnounList rows for categoryPath; root-level nodes and non-category rows for parentTree*). The previous parallel-array form was a workaround for the dataset table view's poor array rendering; the flat-string form is cleaner in JSON and renders the breadcrumb inline in the Overview column.
New primaryImageUrl scalar. First image URL, surfaced as its own scalar so the Apify dataset table renders it as an image thumbnail. The images array still carries the full gallery for programmatic use — but arrays render as "N items" badges in the table view, so the thumbnail needs a scalar. The category-tree categoryIcon field has worked this way since launch; primaryImageUrl brings organic/banner rows to parity.
Notes
No dataset-shape migration path. Consumers reading the JSON output will see slot and status gone, categoryLeafId renamed, breadcrumb arrays replaced by strings, and a new primaryImageUrl field appear. The Actor is not yet published to the public Apify Store (27 dev runs so far), so a clean cut is fine. If you forked this schema for an external pipeline, search-and-replace slot: "organic" filters to categoryPath !== "" (or similar), categoryLeafId → categoryId, and unwrap the breadcrumb arrays (parts = categoryPath.split(" › ")).
[1.0] - 2026-08-28
Removed
rootCategorySlug input field. Merged into categorySlug — see below. Any saved task or API caller passing rootCategorySlug must update the input key to categorySlug. The semantic ("start the category walk at this sub-tree") is preserved; only the field name changed.
Changed
categorySlug is now a single field that covers three modes. In category mode it's the category to browse; in topAnnounList it scopes the banner; in categories it scopes the tree walk. The previous schema exposed categorySlugandrootCategorySlug as separate fields, which made users guess which one to fill for a given mode. They are now one field whose meaning depends on startMode. Field description in the input schema spells out the three modes explicitly. Ignored in search and directUrls. The internal QueuedOperation.vars.categorySlug was already a single name, so the engine did not need a code change.
Pagination framing generalized across search and category. Search and category browse share the exact same SearchAnnouncementsQuery operation with filter.page + paginatorInfo (verified in packages/sites/src/ouedkniss.ts:paginatorOf and profiles/ouedkniss.md). The previous wording emphasized pagination as a category-only feature ("Category browse (with pagination)", "browse a category with pagination" in the description). The category-specific qualifier has been dropped everywhere; both rows in the README's mode table now mention pagination explicitly, and the input schema's startMode.enumTitles for the category mode is just "Category browse".
[0.9] - 2026-08-28
Changed
Memory defaults dropped from 1 GB to 256 MB. Observed peak across every startMode is ~110 MB and CPU stays under 20%, so the platform's 1024 MB default was paying for four times more capacity than the workload needs. The Actor now declares defaultMemoryMbytes: 256 with min: 128 and max: 2048. Apify rounds the value to the nearest power of 2 either way; this just makes the default match the workload. The CPU share scales linearly (256 MB ≈ 1/16 core), but the workload is single-threaded HTTP I/O so the smaller core is enough. You can still raise memory per run if you push a huge directUrls list.
Description no longer claims "no proxy". The earlier description said "no headless browser, no proxy"; the proxy requirement (OuedKniss's edge blocks the default datacenter egress) was already called out in the README and the user-visible note, but the description was stale. It now matches the README — five modes, proxy-agnostic phrasing — and no longer mentions "headless browser".
Notes
Run timeout is not part of actor.json. Apify's actor.json schema only carries memory defaults; the run timeout lives on the Actor's Console settings and on each run invocation. The platform default for the js-crawlee-cheerio template is 300 s, which is fine for the default caps (maxItems: 200, maxPages: 20) but too tight for large runs (each page is rate-limited to maxRequestsPerMinute requests/min, so maxItems: 1000 ≈ 5 min of network alone, plus startup and proxy probing). For long runs, set a longer timeout when you start the run (UI "Timeout" field, or timeoutSecs in the API call). A good rule of thumb is (maxPages / maxRequestsPerMinute) × 60 + 60 seconds, with a floor of 300 s.
[0.8] - 2026-08-28
Fixed
categories mode still failed every push with Schema validation failed after the 0.7 row-shape fix. The site's GraphQL Category.delivery field is a Boolean (was being typed as string | null in the adapter, then categoryDelivery: false was getting rejected by the schema's [string, null] type union). categoryDelivery now types as [boolean, null] in both the schema and the dataset rows.
[0.7] - 2026-08-28
Fixed
categories mode failed every push with Schema validation failed. The dataset schema's required list (url, title, scrapedAt) didn't match what the category-tree walker emitted — those rows are tree nodes, not listings, so they don't have a url or a title. Every request retried 4× before failing, so a single categories run produced zero output on Apify.
Added
slot: "category" discriminator. Category-tree nodes from startMode: "categories" now use the same row shape as organic/topAnnounList listings, with slot set to "category". Filter the dataset with slot === "category" to keep only the tree.
Category-mode fields on every row (null/empty outside category mode):categoryName, categorySlug, categoryIcon, categoryDelivery, categoryRank, categoryDepth, and three parallel parentTreeIds/parentTreeNames/parentTreeSlugs arrays for the breadcrumb from root to (excluding) the current node.
A Categories dataset view. Table projection tuned for the tree walk: name, slug, rank, depth, parent path.
Changed
Required fields on the dataset.required is now ["slot", "id", "scrapedAt"] (was ["slot", "id", "url", "title", "scrapedAt"]). Every row has these; everything else is per-slot.
[0.6] - 2026-08-28
Removed
sponsored field from every output row. The field was true for ~100% of rows because the site returns the same value for organic and paid listings. The field added no information; it was just misleading noise.
includeTopAnnounList input. The sponsored/organic split is gone — see "Added" below.
maxPromotedItems input. No longer needed — see "Added" below.
maxOrganicItems input. Replaced by maxItems. Almost everything this Actor pushes is organic, so the per-mode cap is now maxItems. Default raised from 0 to 200 for visibility.
Added
topAnnounList is now its own startMode. The cross-listing paid banner used to be an opt-in add-on (includeTopAnnounList: true) bolted onto search / category runs. Banner entries have a much sparser response than organic listings (only title, price, store metadata, and one image), so they're now a dedicated mode. Run with { "startMode": "topAnnounList" }. Pass categorySlug to scope the banner to a category.
slot discriminator on every output row. Either "organic" (search / category / directUrls / categories results) or "topAnnounList" (banner entries). Filter the dataset into its streams.
storeId, storeName, storeImageUrl, storeProfileUrl, likeCount fields. Populated only for topAnnounList rows; null for organic ones.
Changed
maxItems is now the standard cap.maxOrganicItems (formerly the per-mode cap) is gone.
startMode enum now includes "topAnnounList".
[0.5] - 2026-08-28
Added
proxyMaxAttempts input field. How many proxy IPs to try at the start of a run when the site blocks the actor's exit IP. The first one that returns data is reused for the rest of the run. Default 5. No effect on local runs.
Run log shows proxy outcome.Network: proxy=on is the success signal; the proxy=off variants cover the known failure modes.
Removed
sourceSite field from every output row. Was always the constant "ouedkniss".
price.currency field from every output row. Was always "DZD".
Changed
Flat output schema. Every top-level field is now a scalar or a list of scalars (the only intentional exception is attributes, which holds free-form category-specific specs). The seller.phones array is exposed as five parallel scalar arrays (sellerPhoneNumbers, sellerPhoneIntls, sellerPhoneWhatsapps, sellerPhoneVibers, sellerPhoneTelegrams) — index N pairs across them.
Flat filter input. The previous filters: { regionIds, priceRange, ... } object is replaced with top-level scalar fields prefixed filter* (filterRegionIds, filterPriceMin, filterHasPictures, …).
startUrls is now string[] instead of Array<{ url, method }>. Each item is just a detail-page URL.
Dropped redundant output fields.price.raw (derivable from priceAmount), location.raw (derivable from wilaya/commune/street), media.imageCount (derivable from images.length), media.videoThumbnail (only useful with video), seller.memberSince (always null), seller.messengerLink (derivable from phones + WhatsApp), category.leafLabel (last item of categoryPath).
[0.4] - 2026-08-27
Fixed
Every request to OuedKniss from Apify returned 200 with a 0-byte body. The site blocks the default datacenter egress IP range. All API calls now route through Apify Proxy.
Changed
Local runs skip proxy creation entirely. Previously the Actor tried to create a proxy configuration even when running locally; now it only does so on real Apify containers.
[0.3] - 2026-08-27
Fixed
Search returned zero results when filter booleans defaulted to false. The SearchFilterInput.exchange and SearchFilterInput.delivery fields only accept null (or omission) — the server returns VALIDATION_INVALID_TYPE_VARIABLE for any other value, including false. The adapter now treats false the same as "unset" so the form's default of false no longer breaks every run.
Changed
Version format dropped the patch segment. Apify's UI displays the first two segments only (M.m), so 0.2.0 was being shown as a malformed version. Bumped to the two-segment form 0.3.
[0.2.0] - 2026-08-26
Changed
Migrated to monorepo layout. This Actor is now actors/ouedkniss/ in the dz-ecommerce-scrapers monorepo, importing the shared engine from @dz/engine and the per-site adapter from @dz/sites. Behavior is unchanged.
Two independent output streams.maxItems and maxPromotedItems are independent caps for the organic and promoted streams. maxItems still works as a global fallback when both are unset.
Added
Per-actor README (actors/ouedkniss/.actor/README.md) with Tutorial, FAQ, Pricing sections — rendered on the Actor's Apify Console and Store pages.
[0.1.0] - 2026-08-25
Added
Initial release. Four run modes: search, category, directUrls, categories.
Optional contact enrichment (includeContactInfo) — no proxy required.