Finish Line APIfinishline.com ↗
Access Finish Line sneaker catalog data via 6 endpoints: search products, browse categories, get release dates, product details, store locations, and suggestions.
What is the Finish Line API?
The Finish Line API covers 6 endpoints that expose product search, category browsing, sneaker release calendars, and store locations from finishline.com. The search_products endpoint returns paginated results with fields including price, original_price, brand, color, rating, and review_count. The get_product_details endpoint goes further, returning SKU-level stock_level values (HIGH, LOW, OOS, UNKNOWN) and available sizes for any product detail page URL.
curl -X GET 'https://api.parse.bot/scraper/de510481-7bb5-4661-9ec5-2884b255e511/search_products?page=0&limit=3&query=Jordan' \ -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 finishline-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.
"""
Finish Line API Parser Client
A practical example for extracting product data, inventory, and release information
from Finish Line using the Parse API.
Get your API key from: https://parse.bot/settings
"""
import os
import requests
from typing import Optional, List, Dict, Any
from datetime import datetime
class ParseClient:
"""Client for interacting with the Finish Line Parse API."""
def __init__(self, api_key: Optional[str] = None):
"""
Initialize the Parse client.
Args:
api_key: API key for authentication. Falls back to PARSE_API_KEY env var.
"""
self.base_url = "https://api.parse.bot"
self.scraper_id = "de510481-7bb5-4661-9ec5-2884b255e511"
self.api_key = api_key or os.getenv("PARSE_API_KEY")
if not self.api_key:
raise ValueError("API key must be provided or set in PARSE_API_KEY environment variable")
def _call(self, endpoint: str, method: str = "POST", **params) -> Any:
"""
Make a request to the Parse API.
Args:
endpoint: The endpoint name (e.g., 'search_products')
method: HTTP method ('GET' or 'POST')
**params: Query or body parameters
Returns:
JSON response from the API
"""
url = f"{self.base_url}/scraper/{self.scraper_id}/{endpoint}"
headers = {
"X-API-Key": self.api_key,
"Content-Type": "application/json"
}
try:
if method == "GET":
response = requests.get(url, headers=headers, params=params)
elif method == "POST":
response = requests.post(url, headers=headers, json=params)
else:
raise ValueError(f"Unsupported HTTP method: {method}")
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"API Error: {e}")
raise
def search_products(
self,
query: str,
page: int = 0,
limit: int = 20
) -> Dict[str, Any]:
"""
Search for products by keyword.
Args:
query: Search keyword (e.g., 'Jordan', 'Nike')
page: Page number for pagination (default: 0)
limit: Max results to return (default: 20)
Returns:
Dictionary with hits, total_hits, total_pages, and page
"""
return self._call("search_products", method="GET", query=query, page=page, limit=limit)
def get_product_details(self, url: str) -> Dict[str, Any]:
"""
Fetch detailed product information from a PDP URL.
Args:
url: Full URL of the product detail page
Returns:
Dictionary with product details including name, price, sizes, colors, description
"""
return self._call("get_product_details", method="GET", url=url)
def get_search_suggestions(self, query: str) -> List[Dict[str, Any]]:
"""
Get search autocomplete suggestions and product previews.
Args:
query: Partial search term
Returns:
List of suggestions with name, image, and url
"""
return self._call("get_search_suggestions", method="GET", query=query)
def get_sneaker_releases(self) -> Dict[str, Any]:
"""
Retrieve upcoming sneaker release dates and details.
Returns:
Dictionary with releases array containing upcoming releases
"""
return self._call("get_sneaker_releases", method="GET")
def get_store_locator(
self,
query: Optional[str] = None,
lat: Optional[str] = None,
lng: Optional[str] = None
) -> List[Dict[str, Any]]:
"""
Search for physical store locations by city name or geolocation.
Args:
query: City name to search near
lat: Latitude for geolocation search
lng: Longitude for geolocation search
Returns:
List of store locations with details
"""
params = {}
if query:
params["query"] = query
if lat:
params["lat"] = lat
if lng:
params["lng"] = lng
return self._call("get_store_locator", method="GET", **params)
def get_product_listing_page(
self,
category_slug: str,
page: int = 0,
limit: int = 20
) -> Dict[str, Any]:
"""
Browse products by category.
Args:
category_slug: Category identifier (e.g., 'mens-shoes', 'womens-shoes')
page: Page number for pagination (default: 0)
limit: Max results to return (default: 20)
Returns:
Dictionary with hits array and total count
"""
return self._call(
"get_product_listing_page",
method="GET",
category_slug=category_slug,
page=page,
limit=limit
)
def main():
"""Demonstrate a practical workflow using the Finish Line API."""
# Initialize the client
client = ParseClient()
print("✓ Connected to Finish Line API Parser\n")
# Workflow: Search for sneakers, get details, find local stores, check releases
print("=" * 80)
print("WORKFLOW: Sneaker Search → Inventory Check → Store Locator → Release Calendar")
print("=" * 80)
# Step 1: Search for Jordan shoes
print("\n[1/5] Searching for 'Jordan basketball shoes'...")
search_results = client.search_products(query="Jordan basketball shoes", limit=5)
if not search_results.get("hits"):
print("No results found.")
return
total_hits = search_results.get("total_hits", 0)
total_pages = search_results.get("total_pages", 0)
current_page = search_results.get("page", 0)
print(f"✓ Found {total_hits} results across {total_pages} pages")
print(f" Showing page {current_page} with {len(search_results['hits'])} items\n")
# Step 2: Analyze top 2 products with detailed inventory info
print("[2/5] Fetching inventory and detailed specs for top 2 products...\n")
selected_products = []
for idx, product in enumerate(search_results["hits"][:2], 1):
product_name = product.get("name", "Unknown")
product_url = product.get("url")
product_price = product.get("price", "N/A")
brand = product.get("brand", "Unknown")
color = product.get("color", "Multiple")
rating = product.get("rating", "N/A")
review_count = product.get("review_count", 0)
style_code = product.get("style_code", "")
print(f" Product {idx}: {product_name}")
print(f" Brand: {brand} | Style: {style_code}")
print(f" Price: ${product_price} | Color: {color}")
print(f" Rating: {rating}/5 ({review_count} reviews)")
if product_url:
try:
details = client.get_product_details(url=product_url)
selected_products.append({
"name": product_name,
"price": product_price,
"url": product_url,
"details": details
})
# Display size availability
if details.get("sizes"):
sizes_data = details["sizes"]
in_stock = [s.get("size") for s in sizes_data if s.get("stock_level") != "OOS"]
print(f" ✓ Available sizes: {', '.join(in_stock[:10])}")
# Count stock levels
stock_summary = {}
for size in sizes_data:
level = size.get("stock_level", "UNKNOWN")
stock_summary[level] = stock_summary.get(level, 0) + 1
print(f" Stock levels: {', '.join([f'{k}={v}' for k, v in stock_summary.items()])}")
# Display color options
if details.get("colors"):
colors_list = [c.get("name") for c in details["colors"][:3]]
print(f" ✓ Colors: {', '.join(colors_list)}")
except Exception as e:
print(f" ✗ Could not fetch details: {str(e)}")
print()
# Step 3: Get related product suggestions
print("[3/5] Getting search suggestions for 'Nike Air'...\n")
suggestions = client.get_search_suggestions(query="Nike Air")
if suggestions and isinstance(suggestions, list):
print(f"✓ Found {len(suggestions)} related product suggestions:")
for suggestion in suggestions[:3]:
sugg_name = suggestion.get("name", "Unknown")
print(f" • {sugg_name}")
print()
# Step 4: Find nearby stores in Chicago
print("[4/5] Locating Finish Line stores in Chicago...\n")
stores = client.get_store_locator(query="Chicago")
if stores and isinstance(stores, list):
print(f"✓ Found {len(stores)} store(s):\n")
for store in stores[:3]:
store_name = store.get("name", "Store")
city = store.get("city", "Unknown")
state = store.get("state", "")
zip_code = store.get("zip", "")
store_id = store.get("store_id", "")
print(f" Store ID: {store_id} | {store_name}")
print(f" Location: {city}, {state} {zip_code}\n")
else:
print("No stores found in that area.\n")
# Step 5: Check upcoming sneaker releases
print("[5/5] Checking upcoming sneaker releases...\n")
releases = client.get_sneaker_releases()
if releases and releases.get("releases"):
total_releases = releases.get("total", 0)
print(f"✓ Found {total_releases} total releases in calendar\n")
# Show upcoming (not yet released) releases
upcoming = [r for r in releases["releases"] if not r.get("released", False)][:4]
if upcoming:
print(f"Next {len(upcoming)} upcoming releases:\n")
for release in upcoming:
rel_name = release.get("name", "Unknown")
rel_date = release.get("release_date", "TBA")
rel_price = release.get("price", "N/A")
brand = release.get("brand", "Unknown")
color = release.get("color", "Multiple")
exclusive = release.get("exclusive_access", False)
# Format release date
try:
dt = datetime.fromisoformat(rel_date.replace("Z", "+00:00"))
formatted_date = dt.strftime("%B %d, %Y at %I:%M %p")
except (ValueError, AttributeError):
formatted_date = rel_date
exclusive_label = " [EXCLUSIVE ACCESS]" if exclusive else ""
print(f" • {brand} {rel_name}{exclusive_label}")
print(f" Release: {formatted_date}")
print(f" Price: ${rel_price} | Color: {color}\n")
else:
print("No upcoming releases scheduled.\n")
# Step 6: Browse a category
print("[Bonus] Browsing 'mens-shoes' category...\n")
category_results = client.get_product_listing_page(category_slug="mens-shoes", limit=3)
if category_results and category_results.get("hits"):
total = category_results.get("total", 0)
page_count = category_results.get("total_pages", 0)
print(f"✓ Found {total} men's shoes across {page_count} pages\n")
print("Sample items from category:\n")
for item in category_results["hits"][:3]:
item_name = item.get("name", "Unknown")
item_price = item.get("price", "N/A")
item_brand = item.get("brand", "Unknown")
item_rating = item.get("rating", "N/A")
print(f" • {item_brand} {item_name}")
print(f" ${item_price} | Rating: {item_rating}/5\n")
print("=" * 80)
print("✓ Workflow completed successfully!")
print("=" * 80)
if __name__ == "__main__":
main()Search for products by keyword. Returns paginated product results with names, prices, images, brands, and URLs.
| Param | Type | Description |
|---|---|---|
| page | integer | Page number (0-based) |
| limit | integer | Max results per page |
| queryrequired | string | Search keyword (e.g. 'Jordan', 'Nike Dunk') |
{
"type": "object",
"fields": {
"hits": "array of product objects with product_id, style_code, name, price, original_price, image_url, url, brand, color, rating, review_count",
"page": "integer current page number",
"total_hits": "integer total number of matching products",
"total_pages": "integer total number of pages"
},
"sample": {
"data": {
"hits": [
{
"url": "https://www.finishline.com/pdp/mens-air-jordan-retro-5-basketball-shoes/prod2799959/HQ7978/103",
"name": "Men's Air Jordan Retro 5 Basketball Shoes",
"brand": "Jordan",
"color": "White/Metallic Silver/Black",
"price": 215,
"rating": 5,
"image_url": "https://media.finishline.com/s/finishline/HQ7978_103?$Main$",
"product_id": "prod2799959",
"style_code": "HQ7978-103",
"review_count": 781,
"original_price": 215
}
],
"page": 0,
"total_hits": 631,
"total_pages": 211
},
"status": "success"
}
}About the Finish Line API
Product Search and Browsing
The search_products endpoint accepts a query string (e.g., 'Jordan' or 'Nike Dunk') and returns paginated hits with fields including product_id, style_code, name, price, original_price, image_url, url, brand, color, rating, and review_count. Pagination is 0-based via the page and limit parameters. The get_product_listing_page endpoint mirrors this response shape but navigates by category slug — accepted slugs include mens-shoes, womens-clothing, boys-shoes, and similar category-level paths — making it straightforward to enumerate a full category.
Product Details and Inventory
get_product_details takes the full PDP URL returned in any hits[*].url field and returns a richer record: a sizes array with each entry carrying a size, sku, and stock_level (one of HIGH, LOW, OOS, or UNKNOWN), a colors array with style_color and name, a description string, and final and original prices. This is the primary endpoint for inventory checking at the SKU level.
Sneaker Releases and Suggestions
get_sneaker_releases requires no inputs and returns up to 50 entries from the release calendar. Each release object includes name, style_code, price, original_price, brand, release_date, released (boolean), launch type, and exclusive_access flag. The get_search_suggestions endpoint takes a partial query string and returns up to 5 preview objects, each with a name, image URL, and url — useful for typeahead interfaces or quickly resolving a style name to a PDP URL.
Store Locator
get_store_locator accepts either a city name via query or geographic coordinates via lat and lng. It returns up to 20 store records with store_id, name, city, state, zip, lat, and lng. This covers physical Finish Line retail locations in the US.
The Finish Line API is a managed, monitored endpoint for finishline.com — not a raw scraper you maintain. Every endpoint is automatically health-checked on a schedule, and when finishline.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 finishline.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?+
- Monitor sneaker release dates and
exclusive_accessflags from the release calendar to alert users before a drop. - Build a price-drop tracker by comparing
pricevsoriginal_priceacross category pages usingget_product_listing_page. - Check real-time size availability and SKU-level
stock_levelfor a specific shoe usingget_product_details. - Power a sneaker search autocomplete feature using
get_search_suggestionswith partial query input. - Aggregate brand and colorway inventory by scraping
brandandcolorfields fromsearch_productsfor a given keyword. - Find the nearest Finish Line retail location by passing GPS coordinates to
get_store_locator. - Compile a release calendar feed with
style_code,brand,price, andrelease_datefor reseller or enthusiast platforms.
| Tier | Price | Credits/month | Rate limit |
|---|---|---|---|
| Free | $0/mo | 100 | 5 req/min |
| Hobby | $30/mo | 1,000 | 20 req/min |
| Developer | $100/mo | 5,000 | 100 req/min |
One credit = one API call regardless of which marketplace API you call. Exceeding the rate limit returns a 429 response. Authenticate with the X-API-Key header.
Does Finish Line have an official public developer API?+
What stock level values does `get_product_details` return, and what do they mean?+
sizes array returns one of four stock_level strings per SKU: HIGH (ample inventory), LOW (limited stock remaining), OOS (out of stock), or UNKNOWN (status not determinable). This is per-size and per-SKU, not an aggregate product-level flag.How many sneaker releases does `get_sneaker_releases` return, and can it be filtered by brand or date?+
brand, release_date, and a released boolean, so filtering can be applied client-side. You can fork the API on Parse and revise it to add server-side filter parameters.Does the API include user reviews or review text?+
search_products and get_product_listing_page return rating and review_count fields, but individual review text and reviewer details are not exposed by any endpoint. You can fork the API on Parse and revise it to add a review-fetching endpoint.What happens if I pass both `query` and `lat`/`lng` to `get_store_locator`?+
query or geographic coordinates via lat and lng. If both are provided, results reflect whichever the source resolves first — for predictable results, use one input method at a time. The endpoint returns up to 20 stores regardless of input method.