- Implemented database initialization and connection management in `database.ts` using Bun's SQLite. - Created `runs` and `results` tables to track historical analysis metadata and detailed product performance. - Updated `writer.ts` to persist analysis results to the database within a transaction, replacing the previous CSV output logic. - Updated README and `.gitignore` to reflect the new persistent storage capability.
asin-check
Amazon product analysis and lead finder agent. Reads product leads from a CSV/XLSX file, enriches them with Keepa pricing and sales data, caches results in Redis, and runs each product through a local LLM to get an FBA/FBM/SKIP verdict.
Requirements
- Bun runtime
- Redis (local or Docker)
- LM Studio running locally with a model loaded
- Keepa API key (keepa.com)
- Amazon SP-API private app credentials (LWA + refresh token + IAM)
Setup
bun install
cp .env.example .env
# Edit .env and set your KEEPA_API_KEY and SP-API credentials
Usage
bun run src/index.ts <input.csv|xlsx> [--out results.csv]
Examples:
bun run src/index.ts leads.xlsx
bun run src/index.ts leads.csv --out results.xlsx
Quick SP-API connectivity tests:
bun run src/sp-test.ts # Auth + sellers endpoint
bun run src/sp-test.ts B07SN9BHVV # Auth + sellers endpoint + pricing offer check
bun run src/sp-test.ts --sellability B07SN9BHVV # Standalone sellability check
Input file format
Accepts .csv or .xlsx files. Column names are matched case-insensitively. Required column:
| Column | Aliases |
|---|---|
| ASIN | — |
Optional but recommended:
| Column | Aliases |
|---|---|
| Product Name | Name, Title |
| Unit Cost | Cost, Price, Buy Cost |
| Brand | — |
| Category | — |
| Amazon Rank | Amazon Rank, BSR, Sales Rank |
| FBA NET | — |
| Gross Profit $ | Gross Profit |
| Gross Profit % | — |
| MOQ | Min Order Qty |
| MOQ Cost | — |
| Total Qty Avail | Qty Available |
| Link | URL, Source |
Lead-list format aliases (supported):
| Column | Aliases |
|---|---|
| Name | Product Name, Title, Product Title |
| ASIN Link | ASIN URL, Amazon Link |
| Source URL | Source Link, Supplier URL |
| 90 Day Average | 90-day Average, Avg Price 90d, 90d Average |
| Cost | Unit Cost, Buy Cost, Price |
| Selling Price | Sale Price, Sell Price |
| Net Profit | Gross Profit |
| ROI | Gross Profit %, Return on Investment |
| Supplier | Vendor |
| Promo/Coupon Code | Promo Code, Coupon Code |
| Notes | Note |
| Date | Lead Date |
Numeric parsing accepts plain numbers as well as formatted values like $12.50, 1,209.60, and 27.5%.
Pipeline
- Read — parse input file, validate ASINs
- Cache check — look up each ASIN in Redis (24h TTL by default)
- Sellability gate — check all uncached ASINs against SP-API
getListingsRestrictions(concurrency: 5 workers); immediately skip ASINs with statusnot_availableandcanSell=false(no Keepa/fees wasted) - Keepa fetch — batch the sellable (uncached) ASINs in a single API call (up to 100 per request)
- Enrich — fetch SP-API pricing + FBA/FBM fees for sellable ASINs; combine with Keepa data and spreadsheet data
- LLM analysis — send batches of 5 sellable products to LM Studio for FBA/FBM/SKIP verdict; skipped ASINs get auto-SKIP verdict (confidence 100) and bypass LLM entirely
- Output — print results table to console (includes all ASINs), optionally write CSV/XLSX, and persist results to a SQLite database.
Persistent Storage with SQLite
Results from each run are now stored in a SQLite database named results.db in the project root. The SQLite implementation details are handled in src/database.ts. This allows you to:
- Revisit past analysis results.
- Query and analyze historical data.
- Track product performance over time.
The database will automatically be created if it doesn't exist. Two tables are created:
runs: Stores metadata about each analysis run (timestamp, input file, output file, and summary counts).results: Stores detailed analysis results for each product from each run, linked to therunstable.
Output columns
ASIN, Name, Brand, Category, Unit Cost, Current Price, Avg Price 90d, Sales Rank, Rank Avg 90d, Sellers, Monthly Sold, Rank Drops 30d, Rank Drops 90d, FBA Net (sheet), Gross Profit $, Gross Profit %, MOQ, MOQ Cost, Qty Available, FBA Fee, FBM Fee, Referral %, Verdict, Confidence, Reasoning
Environment variables
| Variable | Default | Description |
|---|---|---|
KEEPA_API_KEY |
— | Required. Keepa API key |
SP_API_CLIENT_ID |
— | LWA app client id from Solution Provider Portal |
SP_API_CLIENT_SECRET |
— | LWA app client secret from Solution Provider Portal |
SP_API_REFRESH_TOKEN |
— | Refresh token from self-authorization |
SP_API_REGION |
na |
SP-API endpoint region (na, eu, fe; us is accepted as na) |
SP_API_MARKETPLACE_ID |
ATVPDKIKX0DER |
Marketplace id used for pricing and fee calls (default: US) |
SP_API_SELLER_ID |
— | Seller ID used for listing restrictions eligibility checks |
SP_API_USE_SANDBOX |
false |
Enable SP-API sandbox mode (true/false) |
AWS_ACCESS_KEY_ID |
— | AWS credentials for SigV4 signing (required in most private app setups) |
AWS_SECRET_ACCESS_KEY |
— | AWS credentials for SigV4 signing |
AWS_SESSION_TOKEN |
— | Optional session token when using STS credentials |
REDIS_URL |
redis://localhost:6379 |
Redis connection URL |
LLM_URL |
http://localhost:1234/v1 |
LM Studio API base URL |
LLM_MODEL |
default |
Model name to pass to LM Studio |
CACHE_TTL |
86400 |
Redis cache TTL in seconds |
Notes
- Sellability-first optimization: SP-API
getListingsRestrictionsis checked first to filter out unsellable items before consuming Keepa tokens or running full SP-API pricing/fees queries. This saves API calls and reduces runtime for large lead lists. - SP-API concurrency:
fetchSellabilityBatchlimits concurrent requests to 5 workers to avoid 429 throttling. Pricing+fees fetches also use 5 concurrent workers. - No batch endpoint: Amazon SP-API does not provide batch endpoints for
getListingsRestrictionsorgetMyFeesEstimate*. Concurrency limiting with the library's built-inauto_request_throttledsafety net prevents overwhelming the API. - Keepa rate limiting: The client reads
tokensLeftandrefillRatefrom each API response and waits automatically when tokens are exhausted. With a Pro subscription (1 token/min), all 100 ASINs in a batch cost 1 token. - Redis is optional: If Redis is unavailable the tool runs without caching — every run re-fetches from Keepa.
- SP-API:
src/sp-api.tsprovidesfetchSellability,fetchSellabilityBatch, andfetchSpApiPricingAndFeesfunctions. If SP-API credentials are missing or a call fails, the tool falls back to conservative fee defaults and keeps processing. - Skipped ASINs: Products with
not_availablesellability status andcanSell=falseappear in output with verdictSKIP, confidence 100, and reasoning from the sellability check. They do not consume LLM inference. - Sandbox vs production: When
SP_API_USE_SANDBOX=true, production ASIN calls can be denied. Use sandbox-compatible test data or set it tofalsefor live marketplace connectivity.