Client for several IKEA APIs.
[![Test](https://github.com/vrslev/ikea-api-client/actions/workflows/test.yml/badge.svg)](https://github.com/vrslev/ikea-api-client/actions/workflows/test.yml)
[![Python](https://img.shields.io/pypi/pyversions/ikea_api?label=Python)](https://pypi.org/project/ikea_api)
[![Downloads](https://img.shields.io/pypi/dm/ikea_api?label=Downloads&color=blueviolet)](https://pypi.org/project/ikea_api)
# Features
With this library you can access following IKEA's APIs:
- Cart,
- Home Delivery and Collect Services (actually, Order Capture),
- Items info (3 different services),
- 3D models,
- Purchases (history and order info),
- Search,
- Stock.
Also the package:
- Is **backend agnostic**: choose HTTP library you want (async, too!),
- Is **fully typed and tested**,
- Has optional wrappers around some APIs based on Pydantic.
# Installation
```bash
pip install ikea_api
```
- Use [httpx](https://www.python-httpx.org) β awesome async HTTP library βΒ as backend:
```bash
pip install "ikea_api[httpx]"
```
- Use [requests](https://docs.python-requests.org)Β as backend:
```bash
pip install "ikea_api[requests]"
```
- Use wrappers:
```bash
pip install "ikea_api[wrappers]"
```
- Install everything:
```bash
pip install "ikea_api[all]"
```
# Usage
_ikea_api_ is unusual API client. It decouples I/O from logic for easier testing and maintenance. As a bonus, you can use literally _any_ HTTP library.
Let's have a look at how to work with ikea_api.
```python
import ikea_api
# Constants like country, language, base url
constants = ikea_api.Constants(country="us", language="en")
# Search API
search = ikea_api.Search(constants)
# Search endpoint with prepared data
endpoint = search.search("Billy")
```
As you can see, nothing happened up to this point. Code suggests that we already should get the result of the search but we don't. What happened is `search()` returned a data class that contains information about endpoint that can be interpreted by an endpoint runner. There are two built-in: for [requests](https://docs.python-requests.org) (sync) and [httpx](https://www.python-httpx.org) (async), but you can easily write one yourself.
Here's how you would use _requests_ one:
```python
ikea_api.run(endpoint)
```
And _httpx_ one:
```python
await ikea_api.run_async(endpoint)
```
`ikea_api.run_async()` is async function, so you have to "await" it or run using `asyncio.run()`.
## Endpoints reference
### π Authorization
First time you open ikea.com, guest token is being generated and stored in cookies. Same thing must be done in here before using any endpoint.
This token expires in 30 days.
```python
ikea_api.Auth(constants).get_guest_token()
```
Previously you could login as user (with login and password), but now there's very advanced telemetry that I wouldn't be able to solve in hundred years π€ͺ
### π Cart
With this endpoint you can do everything you can using IKEA's frontend:
```python
cart = ikea_api.Cart(constants, token=...)
```
- Show the cart
```python
cart.show()
```
- Clear it
```python
cart.clear()
```
- Add, update and delete items
```python
cart.add_items({"30457903": 1}) # { item_code: quantity }
cart.update_items({"30457903": 5})
cart.remove_items(["30457903"])
```
- Set and clear coupon
```python
cart.set_coupon(...)
cart.clear_coupon()
```
- and even copy another user's cart.
```python
cart.copy_items(source_user_id=...)
```
You can edit your user's actual cart if you use authorized token (copy-paste from cookies).
> π‘Β There's wrapper that clears current cart and adds items with error handling: if requested item doesn't exist, the function just skips it and tries again.
>
> ```python
> ikea_api.add_items_to_cart( # Function returns items that can't be added. In this case: ['11111111']
> cart=cart,
> items={
> "30457903": 1,
> "11111111": 2, # invalid item that will be skipped
> },
> )
> ```
### π Order Capture
Check pickup or delivery availability. If you need to know whether items are available _in stores_, use [Item availability endpoint](#%F0%9F%9F%A2-item-availability) or [ikea-availability-checker](https://github.com/Ephigenia/ikea-availability-checker).
```python
order = ikea_api.OrderCapture(constants, token=token)
cart_show = run(cart.show())
items = ikea_api.convert_cart_to_checkout_items(cart_show)
checkout_id = run(order.get_checkout(items))
service_area_id = run(
order.get_service_area(
checkout_id,
zip_code="02215",
state_code="MA", # pass State Code only if your country has them
)
)
home_services = run(order.get_home_delivery_services(checkout_id, service_area_id))
collect_services = run(
order.get_collect_delivery_services(checkout_id, service_area_id)
)
```
> π‘Β You can use wrapper to add items to cart (clearing cart before and handling unknown item errors if they appear) and parse response in nice Pydantic models:
>
> ```python
> services = await ikea_api.get_delivery_services(
> constants=constants,
> token=...,
> items={
> "30457903": 1,
> "11111111": 2, # invalid item that will be skipped
> },
> zip_code="101000",
> )
> services.delivery_options # List of parsed delivery services
> services.cannot_add # ['11111111']
> ```
### π¦ Purchases
```python
purchases = ikea_api.Purchases(constants, token=token)
```
#### History
This method requires authentication, so if you don't have authorized token, it won't work.
```python
purchases.history()
# Get all purchases:
purchases.history(take=10000)
# Pagination:
purchases.history(take=10, skip=1)
```
> π‘ Get parsed response with the wrapper:
>
> ```python
> ikea_api.get_purchase_history(purchases) # Returns a list of parsed purchases
> ```
#### Order info
```python
purchases.order_info(order_number=..., email=...)
# If you have authorized token, you can drop email:
purchases.order_info(order_number="111111111")
# The method also has other params but they're mostly internal:
purchases.order_info(
order_number=...,
email=...,
queries=..., # Queries that will be included in request, combine any of: ["StatusBannerOrder", "CostsOrder", "ProductListOrder"]. By default, all of them are included.
# Params below are relevant to ProductListOrder
skip_products=...,
skip_product_prices=...,
take_products=...,
)
```
> π‘ Get parsed response with the wrapper:
>
> ```python
> ikea_api.get_purchase_info( # Returns parsed purchase object. Items are not listed.
> purchases=purchases,
> order_number=...,
> email=...,
> )
> ```
### πͺ Item info
Get item specification by item code (product number or whatever). There are 3 endpoints to do this because you can't get all the data about all the items using only one endpoint.
```python
iows_items = ikea_api.IowsItems(constants)
iows_items.get_items(["30457903"])
ingka_items = ikea_api.IngkaItems(constants)
ingka_items.get_items(["30457903"])
pip_item = ikea_api.PipItem(constants)
pip_item.get_item("30457903")
```
> π‘ You probably won't want to use raw APIs when there's convenient "smart" wrapper that combines them all and parses basic info:
>
> ```python
> ikea_api.get_items(["30457903"])
> ```
### π¦ Item 3D models
Get 3D models by item code.
```python
rotera_item = ikea_api.RoteraItem(constants)
rotera_item.get_item("30221043")
```
### π’ Item availability
Get availability by item code (product number or whatever).
```python
stock = ikea_api.Stock(constants)
stock.get_stock("30457903")
```
### π Search
Search for products in the product catalog by product name. Optionally also specify a maximum amount of returned search results (defaults to 24) and types of required search results.
```python
search = ikea_api.Search(constants)
search.search("Billy")
# Retrieve 10 search results (default is 24)
search.search("Billy", limit=10)
# Configure search results types
search.search(
"Billy",
types=..., # Combine any of: ["PRODUCT", "CONTENT", "PLANNER", "REFINED_SEARCHES", "ANSWER"]
)
```
### π Utilities
#### Parse item codes
Parse item codes from string or list.
```python
assert ikea_api.parse_item_codes("111.111.11") == ["11111111"]
assert ikea_api.parse_item_codes("11111111, 222.222.22") == ["11111111", "22222222"]
assert ikea_api.parse_item_codes("111") == []
```
#### Format item code
Parse item code and format it.
```python
assert ikea_api.format_item_code("11111111") == "111.111.11"
assert ikea_api.format_item_code("111-111-11") == "111.111.11"
assert ikea_api.format_item_code("111.111.11") == "111.111.11"
```
Raw data
{
"_id": null,
"home_page": "https://github.com/vrslev/ikea-api-client",
"name": "ikea-api",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.8,<4.0",
"maintainer_email": "",
"keywords": "ikea,ikea api,api,api client",
"author": "Lev Vereshchagin",
"author_email": "mail@vrslev.com",
"download_url": "https://files.pythonhosted.org/packages/e8/b8/4723a7cea364b506ba147e1ed5d53bb60292ce50aa3b17cd4788c36dabf8/ikea_api-2.1.0.tar.gz",
"platform": null,
"description": "Client for several IKEA APIs.\n\n[![Test](https://github.com/vrslev/ikea-api-client/actions/workflows/test.yml/badge.svg)](https://github.com/vrslev/ikea-api-client/actions/workflows/test.yml)\n[![Python](https://img.shields.io/pypi/pyversions/ikea_api?label=Python)](https://pypi.org/project/ikea_api)\n[![Downloads](https://img.shields.io/pypi/dm/ikea_api?label=Downloads&color=blueviolet)](https://pypi.org/project/ikea_api)\n\n# Features\n\nWith this library you can access following IKEA's APIs:\n\n- Cart,\n- Home Delivery and Collect Services (actually, Order Capture),\n- Items info (3 different services),\n- 3D models,\n- Purchases (history and order info),\n- Search,\n- Stock.\n\nAlso the package:\n\n- Is **backend agnostic**: choose HTTP library you want (async, too!),\n- Is **fully typed and tested**,\n- Has optional wrappers around some APIs based on Pydantic.\n\n# Installation\n\n```bash\npip install ikea_api\n```\n\n- Use [httpx](https://www.python-httpx.org) \u2014 awesome async HTTP library \u2014\u00a0as backend:\n\n```bash\npip install \"ikea_api[httpx]\"\n```\n\n- Use [requests](https://docs.python-requests.org)\u00a0as backend:\n\n```bash\npip install \"ikea_api[requests]\"\n```\n\n- Use wrappers:\n\n```bash\npip install \"ikea_api[wrappers]\"\n```\n\n- Install everything:\n\n```bash\npip install \"ikea_api[all]\"\n```\n\n# Usage\n\n_ikea_api_ is unusual API client. It decouples I/O from logic for easier testing and maintenance. As a bonus, you can use literally _any_ HTTP library.\nLet's have a look at how to work with ikea_api.\n\n```python\nimport ikea_api\n\n# Constants like country, language, base url\nconstants = ikea_api.Constants(country=\"us\", language=\"en\")\n# Search API\nsearch = ikea_api.Search(constants)\n# Search endpoint with prepared data\nendpoint = search.search(\"Billy\")\n```\n\nAs you can see, nothing happened up to this point. Code suggests that we already should get the result of the search but we don't. What happened is `search()` returned a data class that contains information about endpoint that can be interpreted by an endpoint runner. There are two built-in: for [requests](https://docs.python-requests.org) (sync) and [httpx](https://www.python-httpx.org) (async), but you can easily write one yourself.\n\nHere's how you would use _requests_ one:\n\n```python\nikea_api.run(endpoint)\n```\n\nAnd _httpx_ one:\n\n```python\nawait ikea_api.run_async(endpoint)\n```\n\n`ikea_api.run_async()` is async function, so you have to \"await\" it or run using `asyncio.run()`.\n\n## Endpoints reference\n\n### \ud83d\udd11 Authorization\n\nFirst time you open ikea.com, guest token is being generated and stored in cookies. Same thing must be done in here before using any endpoint.\n\nThis token expires in 30 days.\n\n```python\nikea_api.Auth(constants).get_guest_token()\n```\n\nPreviously you could login as user (with login and password), but now there's very advanced telemetry that I wouldn't be able to solve in hundred years \ud83e\udd2a\n\n### \ud83d\uded2 Cart\n\nWith this endpoint you can do everything you can using IKEA's frontend:\n\n```python\ncart = ikea_api.Cart(constants, token=...)\n```\n\n- Show the cart\n\n```python\ncart.show()\n```\n\n- Clear it\n\n```python\ncart.clear()\n```\n\n- Add, update and delete items\n\n```python\ncart.add_items({\"30457903\": 1}) # { item_code: quantity }\n\ncart.update_items({\"30457903\": 5})\n\ncart.remove_items([\"30457903\"])\n```\n\n- Set and clear coupon\n\n```python\ncart.set_coupon(...)\n\ncart.clear_coupon()\n```\n\n- and even copy another user's cart.\n\n```python\ncart.copy_items(source_user_id=...)\n```\n\nYou can edit your user's actual cart if you use authorized token (copy-paste from cookies).\n\n> \ud83d\udca1\u00a0There's wrapper that clears current cart and adds items with error handling: if requested item doesn't exist, the function just skips it and tries again.\n>\n> ```python\n> ikea_api.add_items_to_cart( # Function returns items that can't be added. In this case: ['11111111']\n> cart=cart,\n> items={\n> \"30457903\": 1,\n> \"11111111\": 2, # invalid item that will be skipped\n> },\n> )\n> ```\n\n### \ud83d\ude9b Order Capture\n\nCheck pickup or delivery availability. If you need to know whether items are available _in stores_, use [Item availability endpoint](#%F0%9F%9F%A2-item-availability) or [ikea-availability-checker](https://github.com/Ephigenia/ikea-availability-checker).\n\n```python\norder = ikea_api.OrderCapture(constants, token=token)\n\ncart_show = run(cart.show())\nitems = ikea_api.convert_cart_to_checkout_items(cart_show)\n\ncheckout_id = run(order.get_checkout(items))\nservice_area_id = run(\n order.get_service_area(\n checkout_id,\n zip_code=\"02215\",\n state_code=\"MA\", # pass State Code only if your country has them\n )\n)\nhome_services = run(order.get_home_delivery_services(checkout_id, service_area_id))\ncollect_services = run(\n order.get_collect_delivery_services(checkout_id, service_area_id)\n)\n```\n\n> \ud83d\udca1\u00a0You can use wrapper to add items to cart (clearing cart before and handling unknown item errors if they appear) and parse response in nice Pydantic models:\n>\n> ```python\n> services = await ikea_api.get_delivery_services(\n> constants=constants,\n> token=...,\n> items={\n> \"30457903\": 1,\n> \"11111111\": 2, # invalid item that will be skipped\n> },\n> zip_code=\"101000\",\n> )\n> services.delivery_options # List of parsed delivery services\n> services.cannot_add # ['11111111']\n> ```\n\n### \ud83d\udce6 Purchases\n\n```python\npurchases = ikea_api.Purchases(constants, token=token)\n```\n\n#### History\n\nThis method requires authentication, so if you don't have authorized token, it won't work.\n\n```python\npurchases.history()\n\n# Get all purchases:\npurchases.history(take=10000)\n\n# Pagination:\npurchases.history(take=10, skip=1)\n```\n\n> \ud83d\udca1 Get parsed response with the wrapper:\n>\n> ```python\n> ikea_api.get_purchase_history(purchases) # Returns a list of parsed purchases\n> ```\n\n#### Order info\n\n```python\npurchases.order_info(order_number=..., email=...)\n\n# If you have authorized token, you can drop email:\npurchases.order_info(order_number=\"111111111\")\n\n# The method also has other params but they're mostly internal:\npurchases.order_info(\n order_number=...,\n email=...,\n queries=..., # Queries that will be included in request, combine any of: [\"StatusBannerOrder\", \"CostsOrder\", \"ProductListOrder\"]. By default, all of them are included.\n # Params below are relevant to ProductListOrder\n skip_products=...,\n skip_product_prices=...,\n take_products=...,\n)\n```\n\n> \ud83d\udca1 Get parsed response with the wrapper:\n>\n> ```python\n> ikea_api.get_purchase_info( # Returns parsed purchase object. Items are not listed.\n> purchases=purchases,\n> order_number=...,\n> email=...,\n> )\n> ```\n\n### \ud83e\ude91 Item info\n\nGet item specification by item code (product number or whatever). There are 3 endpoints to do this because you can't get all the data about all the items using only one endpoint.\n\n```python\niows_items = ikea_api.IowsItems(constants)\niows_items.get_items([\"30457903\"])\n\ningka_items = ikea_api.IngkaItems(constants)\ningka_items.get_items([\"30457903\"])\n\npip_item = ikea_api.PipItem(constants)\npip_item.get_item(\"30457903\")\n```\n\n> \ud83d\udca1 You probably won't want to use raw APIs when there's convenient \"smart\" wrapper that combines them all and parses basic info:\n>\n> ```python\n> ikea_api.get_items([\"30457903\"])\n> ```\n\n### \ud83d\udce6 Item 3D models\n\nGet 3D models by item code.\n\n```python\nrotera_item = ikea_api.RoteraItem(constants)\nrotera_item.get_item(\"30221043\")\n```\n\n### \ud83d\udfe2 Item availability\n\nGet availability by item code (product number or whatever).\n\n```python\nstock = ikea_api.Stock(constants)\nstock.get_stock(\"30457903\")\n```\n\n### \ud83d\udd0e Search\n\nSearch for products in the product catalog by product name. Optionally also specify a maximum amount of returned search results (defaults to 24) and types of required search results.\n\n```python\nsearch = ikea_api.Search(constants)\nsearch.search(\"Billy\")\n\n# Retrieve 10 search results (default is 24)\nsearch.search(\"Billy\", limit=10)\n\n# Configure search results types\nsearch.search(\n \"Billy\",\n types=..., # Combine any of: [\"PRODUCT\", \"CONTENT\", \"PLANNER\", \"REFINED_SEARCHES\", \"ANSWER\"]\n)\n```\n\n### \ud83d\udee0 Utilities\n\n#### Parse item codes\n\nParse item codes from string or list.\n\n```python\nassert ikea_api.parse_item_codes(\"111.111.11\") == [\"11111111\"]\nassert ikea_api.parse_item_codes(\"11111111, 222.222.22\") == [\"11111111\", \"22222222\"]\nassert ikea_api.parse_item_codes(\"111\") == []\n```\n\n#### Format item code\n\nParse item code and format it.\n\n```python\nassert ikea_api.format_item_code(\"11111111\") == \"111.111.11\"\nassert ikea_api.format_item_code(\"111-111-11\") == \"111.111.11\"\nassert ikea_api.format_item_code(\"111.111.11\") == \"111.111.11\"\n```\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Client for several IKEA's APIs",
"version": "2.1.0",
"project_urls": {
"Homepage": "https://github.com/vrslev/ikea-api-client",
"Repository": "https://github.com/vrslev/ikea-api-client"
},
"split_keywords": [
"ikea",
"ikea api",
"api",
"api client"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "890978b6de6a8c934eea4ae8e7e71b3dea5f37a33a016c1b099a7ad964310257",
"md5": "a4a22d94eb2763852e24f89723010b35",
"sha256": "abeaad57f0820cb960ba5d9b7f84ebb0b6fc5645768fca4a207215bf3a2bec96"
},
"downloads": -1,
"filename": "ikea_api-2.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "a4a22d94eb2763852e24f89723010b35",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8,<4.0",
"size": 35114,
"upload_time": "2023-05-04T15:43:44",
"upload_time_iso_8601": "2023-05-04T15:43:44.480123Z",
"url": "https://files.pythonhosted.org/packages/89/09/78b6de6a8c934eea4ae8e7e71b3dea5f37a33a016c1b099a7ad964310257/ikea_api-2.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "e8b84723a7cea364b506ba147e1ed5d53bb60292ce50aa3b17cd4788c36dabf8",
"md5": "a5dad0834dd3af19bb7fc31a99bb6b61",
"sha256": "8f2cf19084d7cc89b7807e96b8e6aba8403a76812090347e47a12afdc79a6409"
},
"downloads": -1,
"filename": "ikea_api-2.1.0.tar.gz",
"has_sig": false,
"md5_digest": "a5dad0834dd3af19bb7fc31a99bb6b61",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8,<4.0",
"size": 26602,
"upload_time": "2023-05-04T15:43:47",
"upload_time_iso_8601": "2023-05-04T15:43:47.168099Z",
"url": "https://files.pythonhosted.org/packages/e8/b8/4723a7cea364b506ba147e1ed5d53bb60292ce50aa3b17cd4788c36dabf8/ikea_api-2.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-05-04 15:43:47",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "vrslev",
"github_project": "ikea-api-client",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "ikea-api"
}