BOM Part Availability & Lead-Time Monitor avatar

BOM Part Availability & Lead-Time Monitor

Pricing

from $10.00 / 1,000 part-supplier observeds

Go to Apify Store
BOM Part Availability & Lead-Time Monitor

BOM Part Availability & Lead-Time Monitor

Reconcile a bill of materials with your own Mouser and DigiKey API keys. Resolves exact MPN and packaging, prices the order quantity, and reports stock, lead time, and lifecycle. Keeps a baseline and emits shortage, recovery, lead-time, and lifecycle changes. Demo mode needs no keys.

Pricing

from $10.00 / 1,000 part-supplier observeds

Rating

0.0

(0)

Developer

Olalekan Johnson

Olalekan Johnson

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

11 days ago

Last modified

Security

Limited permissions

Share

Unofficial. Not affiliated with Mouser, DigiKey, or the part manufacturers.

A buyer pastes a bill of materials and their own Mouser and DigiKey API credentials. This Actor asks those official APIs whether the exact manufacturer part number, in the packaging on the line, can cover the quantity that must actually be ordered. It prices the break that applies at that quantity, compares distributor stock with the order, and keeps a baseline so the next run can report a shortage, a recovery, a lead-time change, or a lifecycle change.

It does not place orders. It does not treat another part as electrically equivalent. It does not scrape distributor websites.

Who it is for

Small electronics procurement teams who already have a Mouser Search API key, a DigiKey Product Information API app, or both, and who need a repeatable shortage watch on a real BOM. A one-piece price and a reel of a different suffix are the wrong answer for a build that needs 2,000 pieces of tape-and-reel.

Demo mode is the default

Leave every credential empty and run with the default input. runMode is auto, so the Actor uses a bundled sample response set instead of calling Mouser or DigiKey. The sample is synthetic. It is not a quote.

The default run returns 12 dataset rows (6 sample parts × Mouser and DigiKey). Every row has demo: true, charged: false, and a demoNotice. Part observations are not charged. The only platform charge on a demo run is the start event, if you have enabled it.

The first demo run compares the sample with a bundled previous baseline, so the dataset contains real transition types:

LinePartWhat the sample shows
U1TI LM358DR, 2,000, tape and reelMouser stock 800, so shortage. The 1-piece price is 0.48; the price used is the 1,000-piece break, 0.15. DigiKey reel MOQ 2,500 raises the order and can fulfill. Cut tape is noted and not used.
C12Murata GRM21BR71H104KA01L, 10,000Mouser multiple 4,000 raises the order to 12,000.
U3ST STM32G031K8T6, 500, trayMouser lists tube only, so the tray constraint is unmet and the tube price is not used. DigiKey tray MOQ 160 rounds the order to 640.
U7Analog Devices AD8606ARMZ, 100, tubeMouser lead time moved from 8 weeks to 18. DigiKey lifecycle moved from active to NRND. A replacement mentioned in the sample is not copied into the row.
R4Yageo RC0805FR-0710KL, 5,000Mouser was short and now has stock: recovery.
U9TI SN74LVC1G08DBVR, 1,000, cut tapeMouser returns a different manufacturer and a different suffix. DigiKey has no product. Both are empty shortages. The other MPNs are not recommended.

Coverage on that first run is 9 complete, 1 partial, 2 empty. A later demo run of the same sample still returns 12 rows, and the event counts drop to zero because the baseline was saved. Give each BOM its own stateStoreName if you do not want schedules to share a baseline.

Force this path with runMode: "demo" even if keys are filled in. Demo never sends those keys.

Input

Put the BOM in bomJson, or bomCsv, or a public bomUrl. JSON wins, then CSV, then URL. Column names are case-insensitive. Spaces and underscores are ignored, so Manufacturer Part Number, mpn, and lineId all work.

lineId,manufacturer,mpn,quantity,packaging,currency
U1,Texas Instruments,LM358DR,2000,Tape & Reel,USD
[
{
"lineId": "U1",
"manufacturer": "Texas Instruments",
"mpn": "LM358DR",
"quantity": 2000,
"packaging": "Tape & Reel",
"currency": "USD"
}
]
FieldRequiredWhat it does
runModenoauto (default), demo, or live. Auto is demo until a selected supplier has credentials.
suppliersnoDefault mouser and digikey.
mouserApiKeyfor live MouserSecret. Sent only to api.mouser.com.
digikeyClientId, digikeyClientSecretfor live DigiKeySecrets. Used only to obtain a client-credentials token.
digikeyAccountIdnoSecret. Sent as X-DIGIKEY-Account-Id when your DigiKey app requires it.
digikeySandboxnoCall sandbox-api.digikey.com.
digikeyLocaleSitenoTwo-letter site such as US or DE. Otherwise the site follows the line currency. Prices are still not converted.
currencynoDefault USD when a line omits it.
alertOnlyOnChangenoDataset keeps material changes only. Live mode still charges every successful check. The full set is in the LEDGER key.
stateStoreNamenoNamed key-value store for the baseline. Default bom-part-availability-state.
maxBomLinesnoDefault 200, maximum 500. Extra rows are counted and skipped.
minRequestIntervalMsnoPause between official API calls. Default 250.

A live run with only a Mouser key checks Mouser and marks DigiKey blocked for missing credentials. It does not fill DigiKey from the demo. An unknown supplier name is unsupported and is not scraped.

bomUrl must be http or https on port 80 or 443, with no username or password in the URL. Localhost, .local, .internal, private, loopback, and link-local addresses are rejected, including after a redirect.

How a row is decided

  1. The MPN must match exactly. Case and internal spaces are ignored. Punctuation stays, so SN74LVC1G08DBVR is not SN74LVC1G08DBVRG4.
  2. The manufacturer must match when you supplied one. TI matches Texas Instruments. ST matches STMicroelectronics. Maxim does not match Analog Devices. If you leave the manufacturer blank and two manufacturers share the MPN, the line is ambiguous and neither is chosen.
  3. The packaging constraint selects only that packaging of the same MPN. Tape and reel, partial reel (Digi-Reel, MouseReel), cut tape, tray, tube, bulk, and ammo are recognized. Other packagings are described in nonSelectedPackagingNote and are not used for price or stock.
  4. Order quantity is the BOM quantity, raised to the minimum order quantity when that minimum is higher, then rounded up to the order multiple. A DigiKey cut-tape offer does not inherit the full-reel standard package as its multiple.
  5. The unit price is the highest break at or below that order quantity, in the requested currency. A cheaper 1-piece break is stored as onePieceUnitPrice and is used only when the order really falls on that break (usedOnePiecePrice). A higher tier is never used. Another currency is a mismatch, not a conversion. When DigiKey returns My Pricing, that list wins over standard pricing.
  6. canFulfill is distributor stock versus the order quantity. Factory stock and quantity on order are reported and are not added to available stock.
  7. Lead time comes from the selected packaging. 84 Days becomes 12 weeks. Lifecycle maps NRND, EOL, obsolete, last-time-buy, and DigiKey "Not For New Designs". The worst status wins. Conflicting sources lower the confidence and keep coverage at partial.

mayBeCached is true only when the supplier response Age is greater than zero or X-Cache contains hit. DigiKey is called with Product Details, not keyword search. DigiKey documents that keyword results can be cached for up to 24 hours, so keyword search is not used. Demo rows are never cached quotes.

Coverage is complete only when identity, packaging, currency, price, stock, lead time, and lifecycle are all present on a first-party offer and the manufacturer was supplied. A matched part with a gap is partial. No exact part is empty. An HTTP or credential failure is blocked. A supplier this Actor does not call is unsupported.

equivalentSubstitute is always false.

Output example

This is the Mouser row for U1 from the bundled demo, including the shortage against the bundled baseline. The same keys are present on every row. Missing values are null.

{
"packagingConstraint": "Tape & Reel",
"packagingSelected": "Tape & Reel",
"supplierPartNumber": "595-LM358DR",
"manufacturerMatched": "Texas Instruments",
"mpnMatched": "LM358DR",
"description": "IC OPAMP GP 2 CIRCUIT 8SOIC",
"sourceUrl": "https://www.mouser.com/ProductDetail/Texas-Instruments/LM358DR",
"datasheetUrl": "https://www.mouser.com/datasheet/demo/LM358DR",
"rohsStatus": "RoHS Compliant",
"evidenceHash": "e46e71ed6bb4041d2d7a4fa4ae08e957b5117468ed0fa8870adbd09ae44181a0",
"evidenceSnippet": "{\"currency\":\"USD\",\"leadTime\":\"16 Weeks\",\"lifecycle\":\"active\",\"manufacturer\":\"Texas Instruments\",\"mpn\":\"LM358DR\",\"orderQuantity\":2000,\"packaging\":\"Tape & Reel\",\"quantityAvailable\":800,\"sourceUrl\":\"https://www.mouser.com/ProductDetail/Texas-Instruments/LM358DR\",\"supplier\":\"mouser\",\"supplierPartNumber\":\"595-LM358DR\",\"unitPrice\":\"0.15\"}",
"errorMessage": null,
"priceSource": "standard_pricing",
"orderMultipleSource": "supplier_multiple",
"leadTimeRaw": "16 Weeks",
"lifecycle": "active",
"lifecycleRaw": "Active",
"identityNote": null,
"nonSelectedPackagingNote": null,
"observedPackagings": null,
"sourceCurrency": "USD",
"previousLifecycle": "active",
"previousObservedAt": "2026-09-15T12:00:00Z",
"previousUnitPrice": "0.15",
"unitPrice": "0.15",
"extendedPrice": "300.00",
"onePieceUnitPrice": "0.48",
"nonSelectedMinUnitPrice": null,
"digiReelFee": null,
"priceBreaksJson": "[{\"quantity\":1,\"unitPrice\":\"0.48\",\"currency\":\"USD\"},{\"quantity\":10,\"unitPrice\":\"0.36\",\"currency\":\"USD\"},{\"quantity\":100,\"unitPrice\":\"0.22\",\"currency\":\"USD\"},{\"quantity\":1000,\"unitPrice\":\"0.15\",\"currency\":\"USD\"},{\"quantity\":2500,\"unitPrice\":\"0.12\",\"currency\":\"USD\"}]",
"orderQuantity": 2000,
"quantityAvailable": 800,
"stockVersusRequired": -1200,
"stockVersusOrder": -1200,
"moq": 1,
"orderMultiple": 1,
"standardPackage": null,
"applicableBreakQuantity": 1000,
"factoryStock": 12000,
"quantityOnOrder": 5000,
"httpStatus": 200,
"leadTimeDays": 112,
"leadTimeWeeks": 16,
"previousLeadTimeWeeks": 16,
"canFulfill": false,
"previousCanFulfill": true,
"observationId": "97b4fd8d4b98f1cb93a79f655a928b47",
"runMode": "demo",
"supplier": "mouser",
"bomLineId": "U1",
"lineIndex": 1,
"manufacturer": "Texas Instruments",
"mpn": "LM358DR",
"requiredQuantity": 2000,
"requestedCurrency": "USD",
"coverage": "complete",
"confidence": "high",
"observedAt": "2026-10-23T04:00:00Z",
"substituteNote": "No other part is treated as electrically equivalent. A different package, a near-miss MPN, or a replacement mentioned by the supplier is not a recommendation.",
"affiliation": "Unofficial. Not affiliated with Mouser, DigiKey, or the part manufacturers.",
"baselineSource": "bundled_demo_baseline",
"stateKey": "d-a949bf71a2813cd7808c2b12e2e371462ddb89238c76499824ff78e80b342c0b",
"stateIdentity": "demo|mouser|texas instruments|LM358DR|tape_reel|USD",
"events": "shortage",
"dataSource": "bundled-demo-fixture",
"demoNotice": "DEMO DATA. Computed from a bundled sample response set, not from a live Mouser or DigiKey call. demo=true. Part-supplier observations are not charged in demo mode.",
"fulfillmentReason": "stock_short",
"lifecycleSource": "supplier",
"cacheNote": "Bundled demo fixture. Not a live supplier response, and not a quote.",
"rejectedNonExactCount": 0,
"manufacturerMismatchCount": 0,
"candidateCount": 1,
"demo": true,
"materialChange": true,
"charged": false,
"equivalentSubstitute": false,
"statePersisted": true,
"eventShortage": true,
"eventRecovery": false,
"eventLeadTimeChange": false,
"eventLifecycleChange": false,
"eventPriceChange": false,
"eventFirstObserved": false,
"eventCheckFailed": false,
"identityMatched": true,
"packagingMatched": true,
"usedOnePiecePrice": false,
"currencyMatchesRequest": true,
"marketplace": false,
"moqRaised": false,
"multipleRounded": false,
"deliveredToDataset": true,
"mayBeCached": false,
"sourceReportedAReplacement": false
}

The run's default key-value record OUTPUT for that same demo:

{
"actor": "bom-part-availability-monitor",
"title": "BOM Part Availability & Lead-Time Monitor",
"version": "1.0.0",
"demo": true,
"runMode": "demo",
"finishedAt": "2026-10-23T04:00:00Z",
"bomLineCount": 6,
"invalidLineCount": 0,
"duplicateLinesSkipped": 0,
"truncatedLineCount": 0,
"observationCount": 12,
"datasetRecordCount": 12,
"chargedUnits": 0,
"chargeEvent": "part-supplier-observed",
"suggestedUnitPriceUsd": "0.01",
"stoppedReason": null,
"notDeliveredBecauseChargeLimit": 0,
"coverage": {
"complete": 9,
"partial": 1,
"empty": 2,
"blocked": 0,
"unsupported": 0
},
"events": {
"shortage": 3,
"recovery": 1,
"lead_time_change": 1,
"lifecycle_change": 1,
"price_change": 0,
"first_observed": 0,
"check_failed": 0,
"input_rejected": 0
},
"canFulfillCount": 8,
"cannotFulfillCount": 4,
"unknownFulfillmentCount": 0,
"suppliers": [
"mouser",
"digikey"
],
"stateStoreName": "bom-part-availability-state",
"stateKeysWritten": 12,
"stateKeysPreserved": 0,
"stateUnreadableCount": 0,
"alertOnlyOnChange": false,
"demoNotice": "DEMO DATA. Computed from a bundled sample response set, not from a live Mouser or DigiKey call. demo=true. Part-supplier observations are not charged in demo mode.",
"substitutePolicy": "No other part is treated as electrically equivalent. A different package, a near-miss MPN, or a replacement mentioned by the supplier is not a recommendation.",
"affiliation": "Unofficial. Not affiliated with Mouser, DigiKey, or the part manufacturers.",
"partialDueToChargeLimit": false
}

LEDGER holds every observation from the run. In alert-only mode the dataset is the subset where materialChange is true, and the ledger still has the quiet checks.

Events and the baseline

events is a pipe-separated list, in this order:

EventWhen it is set
shortageThe part could be fulfilled and now cannot, or this is the first observation and it cannot.
recoveryIt could not be fulfilled and now can.
lead_time_changeBoth lead times are numeric and differ by at least one week.
lifecycle_changeBoth lifecycles are known and differ. unknown does not count.
price_changeSame currency and the unit price moved by at least 0.5%.
first_observedNo baseline existed yet.
check_failedThe call was blocked, or the saved baseline could not be read. No shortage is inferred from a missing baseline.
input_rejectedThe line failed validation, or the supplier is unsupported.

Only complete, partial, and empty update the named state store, and only after a live charge succeeds (demo writes without charging). blocked and unsupported leave the previous snapshot in place. A run that ends in an exception does not mark last-successful-run. A run that stops because the charge limit was reached is a partial success: the observations already charged stay as the new baseline, and the parts not reached keep the old one.

State keys are separate for demo and live, so a demo schedule cannot overwrite a live baseline.

Pricing

This Actor uses pay-per-event pricing. There is no per-dataset-item charge, so a row is never billed twice.

EventPriceWhen it is charged
apify-actor-start$0.005Every run, including demo. This is the platform start event.
part-supplier-observed$0.01One live observation whose coverage is complete, partial, or empty.

Demo runs do not charge part-supplier-observed. Blocked, unsupported, and rejected lines are not charged. A repeated observation id in the same run is not charged twice. Alert-only mode still charges each successful live check, because the check is the product. Quiet rows are in LEDGER.

Worked examples:

RunArithmeticTotal
Demo daily test, no keysStart event only$0.005
Pilot: 25 parts, both suppliers, one live run50 × $0.01 + $0.005$0.505
Daily watch: 50 parts, both suppliers, 30 days3,000 × $0.01 + 30 × $0.005$30.15

A maximum charge stops the run cleanly. After the start fee, each further $0.01 pays for one observation. The Actor writes an observation only when the charge succeeds, then stops if that charge reports the limit. An observation the platform refuses is not written and does not replace its baseline. OUTPUT.notDeliveredBecauseChargeLimit counts the parts left unchecked, and partialDueToChargeLimit is true. On a $5 cap, the start fee leaves about $4.995, which pays for 499 observations at $0.01 ($4.99). What happens to the next $0.01 depends on the platform's remaining-budget rule. If that charge is accepted and flagged as the limit, the row is delivered and the run stops. If it is refused, that row is omitted and the run stops. Either way the earlier rows keep their new baseline.

Your Mouser and DigiKey quotas are yours. This price does not include their fees.

Scope and limits

  • Official HTTP APIs only. Mouser Search API partnumber with the exact option, batched up to 10. DigiKey Product Information v4 Product Details, one MPN at a time, after a client-credentials token. Hosts are api.mouser.com, api.digikey.com, and sandbox-api.digikey.com. Redirects are not followed. Calls carry a User-Agent that identifies this Actor and the contact address below.
  • Calls are throttled and retried on 429 and 5xx with backoff. Secrets are redacted from error text.
  • The customer must comply with the Mouser and DigiKey API terms. Do not use the output to build a public parts database. The data belongs to the response you were authorized to request.
  • No ordering, shopping cart, AVL, date-code, or contract-price file. A Digi-Reel fee is reported and is not added into extendedPrice.
  • Empty results are real answers and are charged. They can turn a previously fulfillable part into a shortage.
  • Marketplace offers are used only when no first-party offer matched, and then coverage stays partial.
  • The sample catalog is six parts. A demo MPN outside that catalog is empty with reason not_in_demo_catalog and no invented price.
  • Default memory is 512 MB, with a floor of 256 MB and a ceiling of 1024 MB. A few hundred lines finish well inside a one-hour run timeout.

Integrations

Schedule the Actor daily in Apify. Point a webhook, n8n, or Make scenario at the dataset or at the OUTPUT key.

  • Filter rows where events contains shortage, recovery, lead_time_change, or lifecycle_change.
  • Use alertOnlyOnChange when the webhook should stay quiet, and read LEDGER when you want the full check.
  • Send the flat dataset to Google Sheets. Prices are decimal strings (0.15, 0.0082) so a spreadsheet does not have to guess. Text that could look like a formula is prefixed with a quote.
  • Join back to your BOM on bomLineId or on manufacturer + MPN + packaging + currency.
  • Keep a separate stateStoreName per customer or per BOM.

Run it locally

Python 3.12.

pip install -r requirements.txt
pip install pytest pytest-asyncio
pytest
mkdir -p storage/key_value_stores/default
echo '{}' > storage/key_value_stores/default/INPUT.json
APIFY_LOCAL_STORAGE_DIR="$PWD/storage" python -m src

The empty input prints a demo summary and writes storage/datasets/default/. chargedUnits in OUTPUT is 0.

Questions: plusonecares@gmail.com.

FAQ

Why is the unit price higher than the 1-piece price on the same page? The 1-piece break does not apply to a build that must order thousands. unitPrice is the break at the order quantity. onePieceUnitPrice is there so you can see what was not used.

Will it suggest a drop-in replacement when the part is short? No. A different suffix, a different package, and a replacement named by the supplier are reported as not used. equivalentSubstitute stays false. You decide whether any other part is acceptable.

Does alert-only mode make unchanged days free? No. In live mode every successful observation is charged once. Alert-only mode only changes which rows are copied into the dataset.

A supplier returned 401. Did it wipe yesterday's good stock figure? No. Blocked and unsupported results are not charged and do not replace the baseline. The row explains the failure. If every live row is blocked, the run fails after writing those explanations, and last-successful-run is left as it was.

Can I price EUR from a USD break? No. The line's currency is sent to DigiKey as the currency header, and Mouser prices in another currency are a mismatch. Nothing is converted.

Whose API quota is this? Yours. The keys stay in secret inputs and are sent only to the official hosts.