productmapper 1.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- productmapper-1.2.1/.gitignore +21 -0
- productmapper-1.2.1/LICENSE +21 -0
- productmapper-1.2.1/PKG-INFO +376 -0
- productmapper-1.2.1/README.md +340 -0
- productmapper-1.2.1/pyproject.toml +90 -0
- productmapper-1.2.1/src/productmapper/__init__.py +70 -0
- productmapper-1.2.1/src/productmapper/_version.py +21 -0
- productmapper-1.2.1/src/productmapper/client.py +730 -0
- productmapper-1.2.1/src/productmapper/exceptions.py +179 -0
- productmapper-1.2.1/src/productmapper/models.py +458 -0
- productmapper-1.2.1/src/productmapper/py.typed +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*.egg-info/
|
|
4
|
+
.eggs/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
.venv/
|
|
8
|
+
venv/
|
|
9
|
+
.pytest_cache/
|
|
10
|
+
.mypy_cache/
|
|
11
|
+
.ruff_cache/
|
|
12
|
+
.coverage
|
|
13
|
+
htmlcov/
|
|
14
|
+
.env
|
|
15
|
+
.env.*
|
|
16
|
+
!.env.example
|
|
17
|
+
.DS_Store
|
|
18
|
+
Thumbs.db
|
|
19
|
+
.claude/
|
|
20
|
+
.vscode/
|
|
21
|
+
.idea/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SIKTEC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: productmapper
|
|
3
|
+
Version: 1.2.1
|
|
4
|
+
Summary: Official Python client for the ProductMapper API. Resolve UPC, EAN, GTIN, ASIN or free-text titles into live Amazon catalog listings.
|
|
5
|
+
Project-URL: Homepage, https://product-mapper.com
|
|
6
|
+
Project-URL: Documentation, https://product-mapper.com/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/siktec-lab/product-mapper-py
|
|
8
|
+
Project-URL: Issues, https://github.com/siktec-lab/product-mapper-py/issues
|
|
9
|
+
Author: SIKTEC
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: amazon,asin,barcode,catalog,ean,gtin,product-data,productmapper,sdk,upc
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
24
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Python: >=3.9
|
|
28
|
+
Requires-Dist: httpx<1,>=0.24
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: mypy>=1.8; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=7.4; extra == 'dev'
|
|
33
|
+
Requires-Dist: respx>=0.20; extra == 'dev'
|
|
34
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# ProductMapper Python SDK: UPC to ASIN Lookup API Client
|
|
38
|
+
|
|
39
|
+
[](https://pypi.org/project/productmapper/)
|
|
40
|
+
[](https://pypi.org/project/productmapper/)
|
|
41
|
+
[](https://pypi.org/project/productmapper/)
|
|
42
|
+
[](https://github.com/siktec-lab/product-mapper-py/actions/workflows/ci.yml)
|
|
43
|
+
[](https://peps.python.org/pep-0561/)
|
|
44
|
+
[](https://github.com/astral-sh/ruff)
|
|
45
|
+
[](./LICENSE)
|
|
46
|
+
|
|
47
|
+
Official **Python client** for the [ProductMapper API](https://product-mapper.com). Convert a
|
|
48
|
+
**UPC, EAN, GTIN, ASIN or product title into live Amazon listing data**: price, sales rank (BSR),
|
|
49
|
+
offer counts, Buy Box status, brand, category and images, across 16 Amazon marketplaces.
|
|
50
|
+
|
|
51
|
+
Use it to build **barcode to ASIN lookup**, retail arbitrage tooling, competitor price monitoring,
|
|
52
|
+
catalog enrichment, and product data pipelines, with sync and async clients and full type hints.
|
|
53
|
+
|
|
54
|
+
**[Website](https://product-mapper.com)** -
|
|
55
|
+
**[API Documentation](https://product-mapper.com/docs)** -
|
|
56
|
+
**[Get a Free API Key](https://product-mapper.com/dashboard/api-keys)** -
|
|
57
|
+
**[Node.js SDK](https://github.com/siktec-lab/product-mapper-ts)**
|
|
58
|
+
|
|
59
|
+
## Contents
|
|
60
|
+
|
|
61
|
+
- [Why ProductMapper](#why-productmapper)
|
|
62
|
+
- [Install](#install)
|
|
63
|
+
- [Quick start](#quick-start)
|
|
64
|
+
- [Convert UPC to ASIN](#convert-upc-to-asin)
|
|
65
|
+
- [Bulk UPC to ASIN conversion](#bulk-upc-to-asin-conversion)
|
|
66
|
+
- [Lookup history](#lookup-history)
|
|
67
|
+
- [Async client](#async-client)
|
|
68
|
+
- [Error handling](#error-handling)
|
|
69
|
+
- [Configuration](#configuration)
|
|
70
|
+
- [API reference](#api-reference)
|
|
71
|
+
- [FAQ](#faq)
|
|
72
|
+
|
|
73
|
+
## Why ProductMapper
|
|
74
|
+
|
|
75
|
+
| Feature | Detail |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| Identifier types | UPC, EAN, GTIN, ASIN, free-text title, or `auto` detection |
|
|
78
|
+
| Amazon marketplaces | 16 regions including US, CA, UK, DE, FR, IT, ES, JP, AU, IN |
|
|
79
|
+
| Batch size | Up to 500 identifiers per background job, with CSV export |
|
|
80
|
+
| Data returned | Price, list price, sales rank, offer counts, FBA/merchant split, Buy Box, brand, category, images |
|
|
81
|
+
| Clients | Synchronous and asyncio, both fully type hinted |
|
|
82
|
+
| Python | 3.9 through 3.13 |
|
|
83
|
+
|
|
84
|
+
## Install
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
pip install productmapper
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
uv add productmapper
|
|
92
|
+
# or
|
|
93
|
+
poetry add productmapper
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Requires Python 3.9 or newer. Ships `py.typed` for full editor and mypy support.
|
|
97
|
+
|
|
98
|
+
## Quick start
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
import os
|
|
102
|
+
from productmapper import ProductMapper
|
|
103
|
+
|
|
104
|
+
client = ProductMapper(api_key=os.environ["PRODUCTMAPPER_API_KEY"])
|
|
105
|
+
|
|
106
|
+
result = client.lookup(value="753933140816", type="UPC")
|
|
107
|
+
|
|
108
|
+
print(result.marketplace_id) # "B09Z2J1MP2" (the matched ASIN)
|
|
109
|
+
print(result.title) # "Husky Liners Weatherbeater Floor Mats"
|
|
110
|
+
print(result.price) # 80.99
|
|
111
|
+
print(result.listing_details.sales_rank) # 67364
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Get a free API key at
|
|
115
|
+
[product-mapper.com/dashboard/api-keys](https://product-mapper.com/dashboard/api-keys). Keys look like
|
|
116
|
+
`pm_live_...` and belong in an environment variable, never in source control.
|
|
117
|
+
|
|
118
|
+
The client is also a context manager, which closes the connection pool on exit:
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
with ProductMapper(api_key=...) as client:
|
|
122
|
+
result = client.lookup(value="753933140816")
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Convert UPC to ASIN
|
|
126
|
+
|
|
127
|
+
`type` defaults to `auto`, so the server detects whether you passed a UPC, EAN, GTIN or ASIN.
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
client.lookup(value="753933140816") # auto-detected
|
|
131
|
+
client.lookup(value="753933140816", type="UPC") # UPC to ASIN
|
|
132
|
+
client.lookup(value="0885909950805", type="EAN") # EAN to ASIN
|
|
133
|
+
client.lookup(value="B09Z2J1MP2", type="ASIN") # ASIN lookup
|
|
134
|
+
client.lookup(value="Logitech MX Master 3S", type="Title") # title search
|
|
135
|
+
client.lookup(value="753933140816", region="DE") # scope to one marketplace
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Each successful mapping costs one credit. Without a `region`, an identifier that matches in several
|
|
139
|
+
Amazon marketplaces returns them all, and still costs a single credit. Use `.matches` to handle the
|
|
140
|
+
one-match and many-match cases the same way:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
result = client.lookup(value="753933140816")
|
|
144
|
+
|
|
145
|
+
for match in result.matches:
|
|
146
|
+
print(match.amazon_marketplace_label, match.listing_details.formatted_price)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Full listing fields
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
listing = result.listing_details
|
|
153
|
+
|
|
154
|
+
listing.asin # "B09Z2J1MP2"
|
|
155
|
+
listing.title # product title
|
|
156
|
+
listing.brand # "Husky Liners"
|
|
157
|
+
listing.price # 80.99
|
|
158
|
+
listing.list_price # 89.99
|
|
159
|
+
listing.formatted_price # "$80.99"
|
|
160
|
+
listing.sales_rank # 67364 (Best Sellers Rank)
|
|
161
|
+
listing.offer_count # 5
|
|
162
|
+
listing.offer_count_fba # 1
|
|
163
|
+
listing.offer_count_merchant # 4
|
|
164
|
+
listing.is_buy_box_winner # True
|
|
165
|
+
listing.category # "Floor Mats"
|
|
166
|
+
listing.category_group # "Automotive Parts and Accessories"
|
|
167
|
+
listing.image_url # product image
|
|
168
|
+
listing.link # Amazon product URL
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Any field the API adds later is still reachable through `listing.raw["newField"]`.
|
|
172
|
+
|
|
173
|
+
### Slow lookups
|
|
174
|
+
|
|
175
|
+
If a lookup takes more than 8 seconds the API returns a job instead of a result. The client polls that
|
|
176
|
+
job automatically, so `lookup()` always returns a result. To manage polling yourself:
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
queued = client.lookup(value="753933140816", poll=False)
|
|
180
|
+
|
|
181
|
+
if queued.status == "processing":
|
|
182
|
+
result = client.wait_for_job(queued.job_id)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Bulk UPC to ASIN conversion
|
|
186
|
+
|
|
187
|
+
Submit up to 500 identifiers as one background job:
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
job = client.lookup_many(["753933140816", "B09Z2J1MP2", "Logitech MX Master 3S"])
|
|
191
|
+
|
|
192
|
+
finished = client.wait_for_batch(
|
|
193
|
+
job.id,
|
|
194
|
+
on_progress=lambda j: print(f"{j.processed_items}/{j.total_items}"),
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
for item in finished.items:
|
|
198
|
+
print(item.identifier_value, item.title, item.price, item.status)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Export results as CSV, ready for Excel or Google Sheets:
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
from pathlib import Path
|
|
205
|
+
|
|
206
|
+
csv_text = client.get_batch_csv(job.id)
|
|
207
|
+
Path("asin-results.csv").write_text(csv_text, encoding="utf-8")
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Batch rows carry fewer fields than a single lookup. Look an identifier up individually when you need
|
|
211
|
+
`link`, `category`, `identifiers` or the full offer breakdown.
|
|
212
|
+
|
|
213
|
+
## Lookup history
|
|
214
|
+
|
|
215
|
+
Every lookup is recorded, 25 rows per page.
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
page = client.history(page=1, search="husky")
|
|
219
|
+
print(page.total, page.total_pages)
|
|
220
|
+
|
|
221
|
+
for row in page:
|
|
222
|
+
print(row.identifier_value, row.title, row.status)
|
|
223
|
+
|
|
224
|
+
# Or walk every page, one row at a time.
|
|
225
|
+
for row in client.history_all():
|
|
226
|
+
print(row.identifier_value)
|
|
227
|
+
|
|
228
|
+
client.delete_history_row(row_id)
|
|
229
|
+
client.clear_history()
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
History rows report `status` as `success` or `not_found`, while batch items report `completed`.
|
|
233
|
+
|
|
234
|
+
## Async client
|
|
235
|
+
|
|
236
|
+
`AsyncProductMapper` mirrors the sync client method for method.
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
import asyncio
|
|
240
|
+
from productmapper import AsyncProductMapper
|
|
241
|
+
|
|
242
|
+
async def main():
|
|
243
|
+
async with AsyncProductMapper(api_key=...) as client:
|
|
244
|
+
result = await client.lookup(value="753933140816", type="UPC")
|
|
245
|
+
print(result.title)
|
|
246
|
+
|
|
247
|
+
# Resolve many identifiers concurrently.
|
|
248
|
+
results = await asyncio.gather(
|
|
249
|
+
*(client.lookup(value=v) for v in ["753933140816", "B09Z2J1MP2"]),
|
|
250
|
+
return_exceptions=True,
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
async for row in client.history_all():
|
|
254
|
+
print(row.identifier_value)
|
|
255
|
+
|
|
256
|
+
asyncio.run(main())
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Error handling
|
|
260
|
+
|
|
261
|
+
Every failure is a `ProductMapperError`, so one `except` can cover them all, with subclasses for the
|
|
262
|
+
cases worth handling individually.
|
|
263
|
+
|
|
264
|
+
```python
|
|
265
|
+
from productmapper import (
|
|
266
|
+
NotFoundError,
|
|
267
|
+
RateLimitError,
|
|
268
|
+
CreditsExhaustedError,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
try:
|
|
272
|
+
result = client.lookup(value="753933140816")
|
|
273
|
+
except NotFoundError:
|
|
274
|
+
print("No match in the Amazon catalog.")
|
|
275
|
+
except CreditsExhaustedError:
|
|
276
|
+
print("Out of credits: upgrade the plan or buy a credit pack.")
|
|
277
|
+
except RateLimitError as exc:
|
|
278
|
+
print(f"Retry in {exc.retry_after}s, limit is {exc.limit}/min")
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
| Exception | HTTP | Raised when |
|
|
282
|
+
| --- | --- | --- |
|
|
283
|
+
| `ValidationError` | 400 | Bad arguments, rejected before or by the API |
|
|
284
|
+
| `AuthenticationError` | 401 | API key missing, malformed or revoked |
|
|
285
|
+
| `PermissionError` | 403 | No active organization selected |
|
|
286
|
+
| `CreditsExhaustedError` | 403 | Credit balance is empty |
|
|
287
|
+
| `NotFoundError` | 404 | No catalog match, or the resource is not yours |
|
|
288
|
+
| `RateLimitError` | 429 | Plan requests-per-minute exceeded |
|
|
289
|
+
| `ServerError` | 5xx | The API failed to handle the request |
|
|
290
|
+
| `TimeoutError` | - | A request or polling loop ran out of time |
|
|
291
|
+
| `ConnectionError` | - | The request never reached the API |
|
|
292
|
+
| `JobFailedError` | - | A queued lookup or batch ended in a failed state |
|
|
293
|
+
|
|
294
|
+
Rate limits, server errors and network failures are retried automatically with exponential backoff,
|
|
295
|
+
honoring `Retry-After`. Validation and auth failures are never retried.
|
|
296
|
+
|
|
297
|
+
`PermissionError`, `TimeoutError` and `ConnectionError` deliberately shadow the builtins of the same
|
|
298
|
+
name. Import them from `productmapper` to catch the API versions.
|
|
299
|
+
|
|
300
|
+
## Configuration
|
|
301
|
+
|
|
302
|
+
```python
|
|
303
|
+
client = ProductMapper(
|
|
304
|
+
api_key=os.environ["PRODUCTMAPPER_API_KEY"],
|
|
305
|
+
timeout=30.0, # per request, in seconds
|
|
306
|
+
max_retries=2, # for 429, 5xx and network errors
|
|
307
|
+
headers={"X-Team": "pricing"}, # sent with every request
|
|
308
|
+
)
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## API reference
|
|
312
|
+
|
|
313
|
+
| Method | Description |
|
|
314
|
+
| --- | --- |
|
|
315
|
+
| `lookup(value, ...)` | Resolve one identifier. Polls a queued lookup unless `poll=False` |
|
|
316
|
+
| `lookup_many(items, ...)` | Submit up to 500 identifiers as a batch job |
|
|
317
|
+
| `get_job(job_id)` | Poll one queued single lookup |
|
|
318
|
+
| `get_jobs(job_ids)` | Poll up to 100 queued lookups in one round trip |
|
|
319
|
+
| `get_batch(batch_id)` | Fetch a batch job and its items |
|
|
320
|
+
| `get_batch_csv(batch_id)` | Export a batch job as CSV |
|
|
321
|
+
| `wait_for_job(job_id, ...)` | Poll a queued lookup until it resolves |
|
|
322
|
+
| `wait_for_batch(batch_id, ...)` | Poll a batch until every item is processed |
|
|
323
|
+
| `history(page=1, search=None)` | List lookup history, 25 per page |
|
|
324
|
+
| `history_all(search=None)` | Iterator over every history row |
|
|
325
|
+
| `delete_history_row(row_id)` | Delete one history row |
|
|
326
|
+
| `clear_history()` | Clear the entire history |
|
|
327
|
+
|
|
328
|
+
**Identifier types:** `auto`, `UPC`, `EAN`, `GTIN`, `ASIN`, `Title`
|
|
329
|
+
|
|
330
|
+
**Amazon marketplaces:** `US`, `CA`, `MX`, `BR`, `UK`, `DE`, `FR`, `IT`, `ES`, `NL`, `PL`, `SE`, `IN`,
|
|
331
|
+
`JP`, `AU`, `SG`
|
|
332
|
+
|
|
333
|
+
Both are exported as `IDENTIFIER_TYPES` and `REGIONS`.
|
|
334
|
+
|
|
335
|
+
## Examples
|
|
336
|
+
|
|
337
|
+
Runnable scripts live in [examples/](./examples): single lookup, batch with progress and CSV export,
|
|
338
|
+
history paging, async usage, and full error handling.
|
|
339
|
+
|
|
340
|
+
## FAQ
|
|
341
|
+
|
|
342
|
+
**How do I convert a UPC to an ASIN in Python?**
|
|
343
|
+
Install the package, create a client with your API key, and call
|
|
344
|
+
`client.lookup(value="<upc>", type="UPC")`. The matched ASIN is `result.marketplace_id`.
|
|
345
|
+
|
|
346
|
+
**Can I look up many barcodes at once?**
|
|
347
|
+
Yes. `lookup_many()` accepts up to 500 identifiers per batch job, and `get_batch_csv()` exports
|
|
348
|
+
results as CSV.
|
|
349
|
+
|
|
350
|
+
**Does it support asyncio?**
|
|
351
|
+
Yes. `AsyncProductMapper` mirrors the sync client method for method.
|
|
352
|
+
|
|
353
|
+
**Which Amazon marketplaces are supported?**
|
|
354
|
+
16 regions, listed above. Pass `region` to scope a lookup, or omit it to search across regions.
|
|
355
|
+
|
|
356
|
+
**Does it work with pandas?**
|
|
357
|
+
Yes. Batch items and history rows expose plain attributes and a `raw` dict, so
|
|
358
|
+
`pd.DataFrame([item.raw for item in job.items])` works directly.
|
|
359
|
+
|
|
360
|
+
**Is there a free plan?**
|
|
361
|
+
Yes, see [pricing](https://product-mapper.com/pricing).
|
|
362
|
+
|
|
363
|
+
**Is there a Node.js version?**
|
|
364
|
+
Yes, [@siktec-lab/productmapper on npm](https://www.npmjs.com/package/@siktec-lab/productmapper)
|
|
365
|
+
([source](https://github.com/siktec-lab/product-mapper-ts)).
|
|
366
|
+
|
|
367
|
+
## Related
|
|
368
|
+
|
|
369
|
+
- [ProductMapper REST API documentation](https://product-mapper.com/docs)
|
|
370
|
+
- [MCP server for AI agents](https://product-mapper.com/docs/api-mcp)
|
|
371
|
+
- [Node.js and TypeScript SDK](https://github.com/siktec-lab/product-mapper-ts)
|
|
372
|
+
- [Report an issue](https://github.com/siktec-lab/product-mapper-py/issues)
|
|
373
|
+
|
|
374
|
+
## License
|
|
375
|
+
|
|
376
|
+
MIT, see [LICENSE](./LICENSE).
|