Bash APIbash.com ↗
Search the bash.com product catalogue and retrieve TFG physical store locations with GPS coordinates, hours, and structured addresses via two REST endpoints.
What is the Bash API?
The Bash.com API exposes two endpoints covering the full bash.com product catalogue and TFG group store network. The search_products endpoint returns up to 43 products per page — including selling price, retail price, discount percentage, brand, and product URL in ZAR — while list_stores delivers structured location data for roughly 4,100 TFG-brand physical stores across South Africa, including GPS coordinates and weekly business hours.
curl -X GET 'https://api.parse.bot/scraper/2ffccffb-e661-4edc-8c4f-0dad9af4b112/search_products?query=sneakers' \ -H 'X-API-Key: $PARSE_API_KEY'
Typed, relational, agent-ready
A generated client with real types, enums, and the links between objects — the structure a flat JSON response can't carry. Autocompletes in your editor and reads cleanly to coding agents.
- Fully typed · autocompletes
- Objects link to objects
- Typed errors & pagination
Typed Python client. Set up the SDK in your uv project, then pull this API’s typed client:
uv add parse-sdk uv run parse init uv run parse add --marketplace bash-com-api
uv run parse add --marketplace pulls a pinned snapshot of this canonical API — it won’t change underneath you. To customize it, subscribe and swap to your own copy.
"""Walkthrough: bash.com SDK — search products and browse stores."""
from parse_apis.bash_com_api import Bash, ProductSort, InputFormatInvalid
client = Bash()
# Search for sneakers sorted by lowest price; cap at 5 results.
for product in client.products.search(query="sneakers", sort=ProductSort.LOWEST_PRICE, limit=5):
print(f"{product.name} — {product.selling_price} {product.currency}"
f" (retail {product.retail_price}, {product.discount_percentage}% off)")
# Drill into the first on-sale product from a broader search.
hit = client.products.search(query="running shoes", sort=ProductSort.NEWEST, limit=10).first()
if hit is not None:
print(f"{hit.name} | brand: {hit.brand} | on_sale: {hit.on_sale}")
print(f" url: {hit.url}")
# List Sportscene stores in Western Cape with business hours.
for store in client.stores.list(store="Sportscene", province="Western Cape", page_size=10, limit=3):
hours_summary = ", ".join(
f"day {h.day_of_week}: {h.opening_time}-{h.closing_time}"
for h in store.business_hours[:2]
)
print(f"{store.name} ({store.city}) — {store.address} | {hours_summary}")
# Demonstrate typed-error handling on an invalid sort value.
try:
client.products.search(query="boots", sort="BadSort", limit=1).first()
except InputFormatInvalid as e:
print(f"Expected error: {e.message}")
print("exercised: products.search / stores.list / InputFormatInvalid")
Searches the bash.com catalogue by free-text keyword and returns one page of products with name, brand, selling/retail price in ZAR (rand, decimal), discount, sale flag, product URL and main image. One upstream page per call (43 products per page as served by the site); `page` selects the page and `pages`/`total` in the response give the boundary. Omitting `page` returns page 1; omitting `sort` uses the site's default relevance ordering. Prices are decoded from the site's cent-based integers. An unknown search term yields an empty `products` array with `total` 0.
| Param | Type | Description |
|---|---|---|
| page | integer | 1-based result page number; valid up to the `pages` value returned by the previous call. |
| sort | string | Result ordering matching the site's Sort menu. Omitted = site default (most popular). |
| queryrequired | string | Free-text search term, e.g. sneakers. |
{
"type": "object",
"fields": {
"page": "integer, current page",
"sort": "site sort code applied to this result",
"pages": "integer, total pages available upstream",
"query": "echo of the search term",
"total": "integer, total matching products",
"per_page": "integer, products per page as served by the site",
"products": "array of product summaries: product_id (site UUID), name, brand, selling_price and retail_price (ZAR decimals), discount_percentage, currency, on_sale, status, url, image_url"
},
"sample": {
"data": {
"page": 2,
"sort": "OrderByScoreDESC",
"pages": 93,
"query": "sneakers",
"total": 3961,
"per_page": 43,
"products": [
{
"url": "https://bash.com/women-s-tomtom-beam-grey-white-sneaker-190605acic5/p",
"name": "Women's TomTom Beam Grey/White Sneaker",
"brand": "TomTom",
"status": "active",
"on_sale": false,
"currency": "ZAR",
"image_url": "https://assets.bash.com/products/800x1067/filters:quality(85):format(webp)/1ccb1be3-e5dc-4785-a1a3-29b383b31199.png",
"product_id": "6ce4f1f4-08c4-4061-84d3-018648ed7a26",
"retail_price": 449.95,
"selling_price": 449.95,
"discount_percentage": 0
}
]
},
"status": "success"
}
}About the Bash API
Product Search
The search_products endpoint accepts a required query string (e.g. sneakers, formal shirts) and returns a paginated result set. Each product object includes product_id (a site UUID), name, brand, selling_price, retail_price (both in ZAR as decimals), discount_percentage, a sale flag, the canonical product_url, and a main_image URL. The response also returns total matching products, pages (total pages available), per_page (43 as served by the site), and the sort code applied. Use the page parameter (1-based) to walk through result pages and the sort parameter to match the site's Sort menu ordering.
Store Finder
The list_stores endpoint returns entries from the Bash store finder, covering TFG-group brands including Sportscene, Markham, Jet, Foschini, and Totalsports. Each store record includes store_id (with leading zeros preserved), name, is_active, structured address fields (street, complement for shop number or centre name, city, province, country, postal_code), a joined single-line address, GPS latitude and longitude, and a hours object with weekly business hours. Filter by store (case-insensitive prefix match on brand name) and/or province (case-insensitive exact match, e.g. Western Cape) to narrow results. The page_size parameter controls records per page and is clamped to a maximum of 500.
Pagination and Coverage
Both endpoints are paginated. For search_products, check the pages field in the first response before iterating. For list_stores, the full unfiltered set is approximately 4,100 stores; applying store or province filters reduces the total and the corresponding pages count. Store IDs retain their original leading-zero formatting, which matters if you need to match records against external TFG systems.
The Bash API is a managed, monitored endpoint for bash.com — not a raw scraper you maintain. Every endpoint is automatically health-checked on a schedule, and when bash.com changes and a check fails, the API is automatically queued for repair and re-verified. It is built to keep working as the site underneath it changes.
This isn't an official bash.com API — it's an independent, maintained REST wrapper over public data. Where the source has no official API (or only a limited one), Parse gives you a stable contract over a source that never promised one, and keeps it current. Need a new endpoint or field? You can revise it yourself in plain English and the agent rebuilds it against the live site in minutes — contributing the change back to the shared API is free.
Will this API break when the source site changes?+
Is this an official API from the source site?+
Can I fix or extend this API myself if I need a new endpoint or field?+
What happens if I call an endpoint that has an issue?+
- Build a price comparison tool that tracks selling_price vs retail_price and discount_percentage for bash.com products over time.
- Generate a store locator map using GPS coordinates and structured address fields from list_stores for TFG-brand outlets.
- Filter Sportscene or Totalsports locations by province to power a regional store-finder widget.
- Monitor which products carry a sale flag for a given search query to support deal-alert notifications.
- Aggregate product brand distribution across a keyword search using the brand field from search_products.
- Export store business hours and addresses for all Markham branches to populate an offline-capable retail app.
- Cross-reference bash.com product URLs and images to build a shoppable catalogue feed for a specific brand.
| Tier | Price | Credits/month | Rate limit |
|---|---|---|---|
| Free | $0/mo | 200 | 5 req/min |
| Hobby | $30/mo | 1,000 | 20 req/min |
| Developer | $100/mo | 5,000 | 100 req/min |
| Team | $300/mo | 20,000 | 300 req/min |
| Company | $1,000/mo | 100,000 | 500 req/min |
Each endpoint has a fixed posted price per successful call — most fall between 1 and 10 credits — shown on this API's page before you run it. Exceeding the rate limit returns a 429 response. Authenticate with the X-API-Key header.
Does bash.com have an official public developer API?+
What does search_products return beyond price — can I get stock or size availability?+
How does the store filter work, and can I filter by city?+
store parameter does a case-insensitive prefix match on the store brand name (e.g. passing Sport matches Sportscene). The province parameter requires a case-insensitive exact match on the province field (e.g. Gauteng). City-level filtering is not currently exposed as a dedicated parameter. You can fork it on Parse and revise to add a city filter against the city field in each store record.