- README: removed a link to an Actor that is no longer public. No code change.
- Input, dataset rows,
OUTPUT and billing are unchanged.
- Pinned
follow-redirects 1.16.2 and minimatch 11.0.3 through overrides, and undici 7.30.1 through the lock, to clear the npm audit alerts. No code change.
- Input, dataset rows,
OUTPUT and billing are unchanged.
- The stop at the charge cap is saved at once. The row that makes the platform report the maximum charge triggers one explicit save of the run state, so a restart before the next periodic save (15 seconds at most) still stops immediately and pushes nothing more.
- A product already saved that shows up again after the stop is counted in
OUTPUT.duplicateProducts, no longer in OUTPUT.droppedAfterStop.
OUTPUT.statusMessage holds the final status message of the run, the same text as the one shown on the run.
- README: a 15-product run usually takes under 30 seconds (measured 10.5, 17.5 and 30.0 seconds on three builds).
- Rows, events and prices are unchanged.
- Billing under a charge cap: rows are saved one at a time (save, then charge), and the run stops at the first charge the platform reports as reaching the maximum. Rows found after that point are neither saved nor billed and are counted in
OUTPUT.droppedAfterStop. Before this change, rows saved in parallel could pass the limit together: a run capped at 0.02 USD saved 16 rows for 12 billed product events.
OUTPUT.rowsCharged is now the platform's figure, read from the run record at the end of the run (chargedEventCounts.product, up to 3 reads 2 seconds apart). It is null outside the Apify platform or when the record cannot be read. The local count of the SDK moves to OUTPUT.chargeRequests.
- The final status message reads "N rows delivered", followed by "M billed (platform)" when the platform's figure was read, and "M charge requests" otherwise. It no longer reports a billed count taken from the local count.
- At start-up on the platform, the log shows the
product price and the maximum charge seen by the SDK next to those of the run record, with a warning pricing mismatch between SDK and run record when they differ.
- A run stopped by the charge cap stays stopped after a migration or a reboot: it makes no request and saves no row.
- Without a charge cap, rows and billing are unchanged.
Next platform build. The Apify build counter already stands at 0.1.2 (first push 0.1.1 on 2026-10-27, second push 0.1.2 on 2026-10-28), so this entry groups everything not yet published: the earlier entries 0.1.1 and 0.1.2 were private test builds.
- Product rows for the US English store of ae.com (American Eagle and Aerie), one row per product colour. Categories are read page by page, the product sitemap is read for
fullCatalogue, and a product that appears in several categories or start URLs gives one row.
- Brands:
brand is AE, Aerie or OFFLINE. Products of the OFFLINE sub-brand ("OFFLINE by Aerie") are normal products and give a billed row, never a TARGET_NOT_FOUND error. TARGET_NOT_FOUND stays for a 404 and a 410 only.
startUrls also accepts a product link without the locale part (https://www.ae.com/p/.../0000_0000_000, the form the site itself returns). It is read as /us/en/p/..., and the two forms of one product count as duplicates. The description of the field lists the accepted forms.
- Sizes:
sizes[] carries the size, the size code, the SKU, both prices and stockLevel, the site's stock code per size (0 available, 2 unavailable, 1 low stock, sometimes still orderable, sometimes not). There is no available field.
- Category listings: when the pages give fewer unique product ids than the announced
totalProducts, all pages are read once more and the new ids are merged. A gap that remains is accepted and reported in OUTPUT.categories[] (categoryId, totalProducts, uniqueIds, reread), with a warning in the log and no row in the errors dataset. A product shown on two pages counts once toward maxProducts.
- Faster and cheaper runs: the work list is kept in memory. The full catalogue (9,505 products) was read in about 17 minutes, and the prefilled example (15 products) in about 18 seconds.
- Resume: after a platform migration or a reboot the Actor reads its own dataset at start-up and skips the products already saved, so a resume is designed to lose no row and bill no row twice. Checked on a full catalogue run with a forced reboot: 26 rows reconciled, 9,505 rows for 9,505 distinct products. In the worst case a row saved just as a stop cut its charge stays in the dataset without being billed.
OUTPUT gains stateFlushes.
- A run that restarts with a saved state now logs
Resumed after migration or restart: N row(s) already emitted. before the reconciliation line. The end-of-run check on the dataset item count (OUTPUT.datasetRows and its warning) is removed because that count lags behind the writes. Rows and billing are unchanged.
- Billing: one
product event per saved row, the row cap maxProducts is reserved before each row is saved, and the run stops cleanly when the maximum charge is reached. Error rows are never charged.
- Sources: the product data that ae.com loads in the browser first, then the product page as a fallback, capped at the larger of 20 pages and 5% of the products started, after which the run stops cleanly. A run without access to the product data reads at most 20 pages, then stops with
stoppedReason: noToken, one TARGET_BLOCKED row per unread start URL and a status message. Secrets are removed from every message.
- Runs end with an
OUTPUT record (stoppedReason, request counts, rows by source, the run errors) and an internal deadline one minute before the platform timeout.
- Input:
startUrls, fullCatalogue, maxProducts (prefill 15), includeSizes, includeDescription and locale (us/en only).
- Dataset views
overview, prices and sizes, and a separate errors dataset.
- Documentation: README with stop reasons, the errors dataset, brand values, the
categories and resume sections, measured durations and the catalogue size (9,510 sitemap pages on 2026-10-28).