# Synthefy Python Client
A Python client for the Synthefy API forecasting service. This package provides an easy-to-use interface for making time series forecasting requests with both synchronous and asynchronous support.
## Features
- **Sync & Async Support**: Separate clients for synchronous and asynchronous operations
- **Professional Error Handling**: Comprehensive exception hierarchy with detailed error messages
- **Retry Logic**: Built-in exponential backoff for transient errors (rate limits, server errors)
- **Context Managers**: Automatic resource cleanup with `with` and `async with` statements
- **Pandas Integration**: Built-in support for pandas DataFrames
- **Type Safety**: Full type hints and Pydantic validation
## Installation
```bash
pip install synthefy
```
## Quick Start
### Basic Usage
```python
from synthefy import SynthefyAPIClient, SynthefyAsyncAPIClient
import pandas as pd
# Synchronous client
with SynthefyAPIClient(api_key="your_api_key_here") as client:
# Make requests...
pass
# Asynchronous client
async with SynthefyAsyncAPIClient() as client: # Uses SYNTHEFY_API_KEY env var
# Make async requests...
pass
```
### Making a Forecast Request
```python
from synthefy import SynthefyAPIClient
import pandas as pd
import numpy as np
# Create sample data with numeric metadata
history_data = {
'date': pd.date_range('2024-01-01', periods=100, freq='D'),
'sales': np.random.normal(100, 10, 100),
'store_id': 1,
'category_id': 101,
'promotion_active': 0
}
target_data = {
'date': pd.date_range('2024-04-11', periods=30, freq='D'),
'sales': np.nan, # Values to forecast
'store_id': 1,
'category_id': 101,
'promotion_active': 1 # Promotion active in forecast period
}
history_df = pd.DataFrame(history_data)
target_df = pd.DataFrame(target_data)
# Synchronous forecast
with SynthefyAPIClient() as client:
forecast_dfs = client.forecast_dfs(
history_dfs=[history_df],
target_dfs=[target_df],
target_col='sales',
timestamp_col='date',
metadata_cols=['store_id', 'category_id', 'promotion_active'],
leak_cols=[],
model='sfm-moe-v1'
)
# Result is a list of DataFrames with forecasts
forecast_df = forecast_dfs[0]
print(forecast_df[['timestamps', 'sales']].head())
```
### Asynchronous Usage
```python
import asyncio
from synthefy.api_client import SynthefyAsyncAPIClient
async def main():
async with SynthefyAsyncAPIClient() as client:
# Single async forecast
forecast_dfs = await client.forecast_dfs(
history_dfs=[history_df],
target_dfs=[target_df],
target_col='sales',
timestamp_col='date',
metadata_cols=['store_id', 'category_id', 'promotion_active'],
leak_cols=[],
model='sfm-moe-v1'
)
# Concurrent forecasts for multiple datasets
tasks = []
for i in range(3):
# Create variations of your data
modified_history = history_df.copy()
modified_target = target_df.copy()
modified_history['store_id'] = i + 1
modified_target['store_id'] = i + 1
task = client.forecast_dfs(
history_dfs=[modified_history],
target_dfs=[modified_target],
target_col='sales',
timestamp_col='date',
metadata_cols=['store_id', 'category_id', 'promotion_active'],
leak_cols=[],
model='sfm-moe-v1'
)
tasks.append(task)
# Execute all forecasts concurrently
results = await asyncio.gather(*tasks)
for i, forecast_dfs in enumerate(results):
print(f"Forecast for store {i+1}: {len(forecast_dfs[0])} predictions")
# Run the async function
asyncio.run(main())
```
### Backtesting
```python
import asyncio
import pandas as pd
import numpy as np
from synthefy.data_models import ForecastV2Request
from synthefy.api_client import SynthefyAsyncAPIClient
async def main():
# Create sample time series data
dates = pd.date_range('2023-01-01', '2023-12-31', freq='D')
data = {
'date': dates,
'sales': np.random.normal(100, 10, len(dates)),
'store_id': 1,
'category_id': 101,
'promotion_active': np.random.choice([0, 1], len(dates), p=[0.7, 0.3])
}
df = pd.DataFrame(data)
print(f"Created dataset with {len(df)} rows from {df['date'].min()} to {df['date'].max()}")
# Use from_dfs_pre_split for backtesting with date-based windows
request = ForecastV2Request.from_dfs_pre_split(
dfs=[df],
timestamp_col='date',
target_cols=['sales'],
model='sfm-moe-v1',
cutoff_date='2023-06-01', # Start backtesting from June 1st
forecast_window='7D', # 7-day forecast windows
stride='14D', # Move forward 14 days between windows
metadata_cols=['store_id', 'category_id', 'promotion_active'],
leak_cols=['promotion_active'] # Promotion data may leak into target
)
print(f"Created {len(request.samples)} forecast windows for backtesting")
print("Window details:")
for i, sample in enumerate(request.samples):
history_start = sample[0].history_timestamps[0]
history_end = sample[0].history_timestamps[-1]
target_start = sample[0].target_timestamps[0]
target_end = sample[0].target_timestamps[-1]
print(f" Window {i+1}: History {history_start} to {history_end}, Target {target_start} to {target_end}")
# Make async forecast request
async with SynthefyAsyncAPIClient() as client:
response = await client.forecast(request)
print(f"\nBacktesting completed with {len(response.samples)} forecast windows")
# Process results for each window
for i, sample in enumerate(response.samples):
print(f"Window {i+1}: {len(sample.history_timestamps)} history points, "
f"{len(sample.target_timestamps)} target points")
# Access forecast values
if hasattr(sample, 'forecast_values') and sample.forecast_values:
print(f" Forecast values: {sample.forecast_values[:3]}...") # First 3 values
asyncio.run(main())
```
### Advanced Configuration
```python
from synthefy import SynthefyAPIClient
from synthefy.api_client import BadRequestError, RateLimitError
# Client with custom configuration
with SynthefyAPIClient(
api_key="your_key",
timeout=600.0, # 10 minutes
max_retries=3,
organization="your_org_id",
base_url="https://custom.synthefy.com" # For enterprise customers
) as client:
try:
# Per-request configuration
forecast_dfs = client.forecast_dfs(
history_dfs=[history_df],
target_dfs=[target_df],
target_col='sales',
timestamp_col='date',
metadata_cols=['store_id'],
leak_cols=[],
model='sfm-moe-v1',
timeout=120.0, # Override client timeout for this request
idempotency_key="unique-request-id", # Prevent duplicate processing
extra_headers={"X-Custom-Header": "value"}
)
except BadRequestError as e:
print(f"Invalid request: {e}")
print(f"Status code: {e.status_code}")
print(f"Request ID: {e.request_id}")
except RateLimitError as e:
print(f"Rate limited: {e}")
# Client automatically retries with exponential backoff
except Exception as e:
print(f"Unexpected error: {e}")
```
## API Reference
### SynthefyAPIClient (Synchronous)
The synchronous client class for interacting with the Synthefy API.
#### Constructor Parameters
- `api_key`: Your Synthefy API key (can also be set via `SYNTHEFY_API_KEY` environment variable)
- `timeout`: Request timeout in seconds (default: 300.0 / 5 minutes)
- `max_retries`: Number of retries for transient errors (default: 2)
- `base_url`: API base URL (default: "https://prod.synthefy.com")
- `organization`: Optional organization ID for multi-tenant setups
- `user_agent`: Custom user agent string
#### Methods
- `forecast(request, *, timeout=None, idempotency_key=None, extra_headers=None) -> ForecastV2Response`
- Make a direct forecast request with a `ForecastV2Request` object
- `forecast_dfs(history_dfs, target_dfs, target_col, timestamp_col, metadata_cols, leak_cols, model) -> List[pd.DataFrame]`
- Convenience method for working directly with pandas DataFrames
- `close()`: Manually close the HTTP client
- Context manager support: Use with `with SynthefyAPIClient() as client:`
### SynthefyAsyncAPIClient (Asynchronous)
The asynchronous client class for non-blocking operations and concurrent requests.
#### Constructor Parameters
Same as `SynthefyAPIClient`.
#### Methods
- `async forecast(request, *, timeout=None, idempotency_key=None, extra_headers=None) -> ForecastV2Response`
- Async version of forecast method
- `async forecast_dfs(history_dfs, target_dfs, target_col, timestamp_col, metadata_cols, leak_cols, model) -> List[pd.DataFrame]`
- Async version of forecast_dfs method
- `async aclose()`: Manually close the async HTTP client
- Async context manager support: Use with `async with SynthefyAsyncAPIClient() as client:`
### Exception Hierarchy
All exceptions inherit from `SynthefyError`:
- `APITimeoutError`: Request timed out
- `APIConnectionError`: Network/connection issues
- `APIStatusError`: Base class for HTTP status errors
- `BadRequestError` (400, 422): Invalid request data
- `AuthenticationError` (401): Invalid API key
- `PermissionDeniedError` (403): Access denied
- `NotFoundError` (404): Resource not found
- `RateLimitError` (429): Rate limit exceeded
- `InternalServerError` (5xx): Server errors
Each status error includes:
- `status_code`: HTTP status code
- `request_id`: Request ID for debugging (if available)
- `error_code`: API-specific error code (if available)
- `response_body`: Raw response body
## Configuration
### Environment Variables
- `SYNTHEFY_API_KEY`: Your Synthefy API key
## Support
For support and questions:
- Email: contact@synthefy.com
## License
MIT License - see LICENSE file for details.
Raw data
{
"_id": null,
"home_page": null,
"name": "synthefy",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": "Synthefy <contact@synthefy.com>",
"keywords": "api, forecasting, machine-learning, time-series",
"author": null,
"author_email": "Synthefy <contact@synthefy.com>",
"download_url": "https://files.pythonhosted.org/packages/c3/07/a54985f20b84dcabd786b8961afd1c4552837ab7bdafda6662355e606440/synthefy-2.0.4.tar.gz",
"platform": null,
"description": "# Synthefy Python Client\n\nA Python client for the Synthefy API forecasting service. This package provides an easy-to-use interface for making time series forecasting requests with both synchronous and asynchronous support.\n\n## Features\n\n- **Sync & Async Support**: Separate clients for synchronous and asynchronous operations\n- **Professional Error Handling**: Comprehensive exception hierarchy with detailed error messages\n- **Retry Logic**: Built-in exponential backoff for transient errors (rate limits, server errors)\n- **Context Managers**: Automatic resource cleanup with `with` and `async with` statements\n- **Pandas Integration**: Built-in support for pandas DataFrames\n- **Type Safety**: Full type hints and Pydantic validation\n\n## Installation\n\n```bash\npip install synthefy\n```\n\n## Quick Start\n\n### Basic Usage\n\n```python\nfrom synthefy import SynthefyAPIClient, SynthefyAsyncAPIClient\nimport pandas as pd\n\n# Synchronous client\nwith SynthefyAPIClient(api_key=\"your_api_key_here\") as client:\n # Make requests...\n pass\n\n# Asynchronous client\nasync with SynthefyAsyncAPIClient() as client: # Uses SYNTHEFY_API_KEY env var\n # Make async requests...\n pass\n```\n\n### Making a Forecast Request\n\n```python\nfrom synthefy import SynthefyAPIClient\nimport pandas as pd\nimport numpy as np\n\n# Create sample data with numeric metadata\nhistory_data = {\n 'date': pd.date_range('2024-01-01', periods=100, freq='D'),\n 'sales': np.random.normal(100, 10, 100),\n 'store_id': 1,\n 'category_id': 101,\n 'promotion_active': 0\n}\n\ntarget_data = {\n 'date': pd.date_range('2024-04-11', periods=30, freq='D'),\n 'sales': np.nan, # Values to forecast\n 'store_id': 1,\n 'category_id': 101,\n 'promotion_active': 1 # Promotion active in forecast period\n}\n\nhistory_df = pd.DataFrame(history_data)\ntarget_df = pd.DataFrame(target_data)\n\n# Synchronous forecast\nwith SynthefyAPIClient() as client:\n forecast_dfs = client.forecast_dfs(\n history_dfs=[history_df],\n target_dfs=[target_df],\n target_col='sales',\n timestamp_col='date',\n metadata_cols=['store_id', 'category_id', 'promotion_active'],\n leak_cols=[],\n model='sfm-moe-v1'\n )\n\n# Result is a list of DataFrames with forecasts\nforecast_df = forecast_dfs[0]\nprint(forecast_df[['timestamps', 'sales']].head())\n```\n\n### Asynchronous Usage\n\n```python\nimport asyncio\nfrom synthefy.api_client import SynthefyAsyncAPIClient\n\nasync def main():\n async with SynthefyAsyncAPIClient() as client:\n # Single async forecast\n forecast_dfs = await client.forecast_dfs(\n history_dfs=[history_df],\n target_dfs=[target_df],\n target_col='sales',\n timestamp_col='date',\n metadata_cols=['store_id', 'category_id', 'promotion_active'],\n leak_cols=[],\n model='sfm-moe-v1'\n )\n\n # Concurrent forecasts for multiple datasets\n tasks = []\n for i in range(3):\n # Create variations of your data\n modified_history = history_df.copy()\n modified_target = target_df.copy()\n modified_history['store_id'] = i + 1\n modified_target['store_id'] = i + 1\n\n task = client.forecast_dfs(\n history_dfs=[modified_history],\n target_dfs=[modified_target],\n target_col='sales',\n timestamp_col='date',\n metadata_cols=['store_id', 'category_id', 'promotion_active'],\n leak_cols=[],\n model='sfm-moe-v1'\n )\n tasks.append(task)\n\n # Execute all forecasts concurrently\n results = await asyncio.gather(*tasks)\n\n for i, forecast_dfs in enumerate(results):\n print(f\"Forecast for store {i+1}: {len(forecast_dfs[0])} predictions\")\n\n# Run the async function\nasyncio.run(main())\n```\n\n### Backtesting\n\n```python\nimport asyncio\nimport pandas as pd\nimport numpy as np\nfrom synthefy.data_models import ForecastV2Request\nfrom synthefy.api_client import SynthefyAsyncAPIClient\n\nasync def main():\n\n # Create sample time series data\n dates = pd.date_range('2023-01-01', '2023-12-31', freq='D')\n data = {\n 'date': dates,\n 'sales': np.random.normal(100, 10, len(dates)),\n 'store_id': 1,\n 'category_id': 101,\n 'promotion_active': np.random.choice([0, 1], len(dates), p=[0.7, 0.3])\n }\n df = pd.DataFrame(data)\n\n print(f\"Created dataset with {len(df)} rows from {df['date'].min()} to {df['date'].max()}\")\n\n # Use from_dfs_pre_split for backtesting with date-based windows\n request = ForecastV2Request.from_dfs_pre_split(\n dfs=[df],\n timestamp_col='date',\n target_cols=['sales'],\n model='sfm-moe-v1',\n cutoff_date='2023-06-01', # Start backtesting from June 1st\n forecast_window='7D', # 7-day forecast windows\n stride='14D', # Move forward 14 days between windows\n metadata_cols=['store_id', 'category_id', 'promotion_active'],\n leak_cols=['promotion_active'] # Promotion data may leak into target\n )\n\n print(f\"Created {len(request.samples)} forecast windows for backtesting\")\n print(\"Window details:\")\n for i, sample in enumerate(request.samples):\n history_start = sample[0].history_timestamps[0]\n history_end = sample[0].history_timestamps[-1]\n target_start = sample[0].target_timestamps[0]\n target_end = sample[0].target_timestamps[-1]\n print(f\" Window {i+1}: History {history_start} to {history_end}, Target {target_start} to {target_end}\")\n\n # Make async forecast request\n async with SynthefyAsyncAPIClient() as client:\n response = await client.forecast(request)\n\n print(f\"\\nBacktesting completed with {len(response.samples)} forecast windows\")\n\n # Process results for each window\n for i, sample in enumerate(response.samples):\n print(f\"Window {i+1}: {len(sample.history_timestamps)} history points, \"\n f\"{len(sample.target_timestamps)} target points\")\n\n # Access forecast values\n if hasattr(sample, 'forecast_values') and sample.forecast_values:\n print(f\" Forecast values: {sample.forecast_values[:3]}...\") # First 3 values\nasyncio.run(main())\n```\n\n### Advanced Configuration\n\n```python\nfrom synthefy import SynthefyAPIClient\nfrom synthefy.api_client import BadRequestError, RateLimitError\n\n# Client with custom configuration\nwith SynthefyAPIClient(\n api_key=\"your_key\",\n timeout=600.0, # 10 minutes\n max_retries=3,\n organization=\"your_org_id\",\n base_url=\"https://custom.synthefy.com\" # For enterprise customers\n) as client:\n try:\n # Per-request configuration\n forecast_dfs = client.forecast_dfs(\n history_dfs=[history_df],\n target_dfs=[target_df],\n target_col='sales',\n timestamp_col='date',\n metadata_cols=['store_id'],\n leak_cols=[],\n model='sfm-moe-v1',\n timeout=120.0, # Override client timeout for this request\n idempotency_key=\"unique-request-id\", # Prevent duplicate processing\n extra_headers={\"X-Custom-Header\": \"value\"}\n )\n except BadRequestError as e:\n print(f\"Invalid request: {e}\")\n print(f\"Status code: {e.status_code}\")\n print(f\"Request ID: {e.request_id}\")\n except RateLimitError as e:\n print(f\"Rate limited: {e}\")\n # Client automatically retries with exponential backoff\n except Exception as e:\n print(f\"Unexpected error: {e}\")\n```\n\n## API Reference\n\n### SynthefyAPIClient (Synchronous)\n\nThe synchronous client class for interacting with the Synthefy API.\n\n#### Constructor Parameters\n\n- `api_key`: Your Synthefy API key (can also be set via `SYNTHEFY_API_KEY` environment variable)\n- `timeout`: Request timeout in seconds (default: 300.0 / 5 minutes)\n- `max_retries`: Number of retries for transient errors (default: 2)\n- `base_url`: API base URL (default: \"https://prod.synthefy.com\")\n- `organization`: Optional organization ID for multi-tenant setups\n- `user_agent`: Custom user agent string\n\n#### Methods\n\n- `forecast(request, *, timeout=None, idempotency_key=None, extra_headers=None) -> ForecastV2Response`\n - Make a direct forecast request with a `ForecastV2Request` object\n- `forecast_dfs(history_dfs, target_dfs, target_col, timestamp_col, metadata_cols, leak_cols, model) -> List[pd.DataFrame]`\n - Convenience method for working directly with pandas DataFrames\n- `close()`: Manually close the HTTP client\n- Context manager support: Use with `with SynthefyAPIClient() as client:`\n\n### SynthefyAsyncAPIClient (Asynchronous)\n\nThe asynchronous client class for non-blocking operations and concurrent requests.\n\n#### Constructor Parameters\n\nSame as `SynthefyAPIClient`.\n\n#### Methods\n\n- `async forecast(request, *, timeout=None, idempotency_key=None, extra_headers=None) -> ForecastV2Response`\n - Async version of forecast method\n- `async forecast_dfs(history_dfs, target_dfs, target_col, timestamp_col, metadata_cols, leak_cols, model) -> List[pd.DataFrame]`\n - Async version of forecast_dfs method\n- `async aclose()`: Manually close the async HTTP client\n- Async context manager support: Use with `async with SynthefyAsyncAPIClient() as client:`\n\n### Exception Hierarchy\n\nAll exceptions inherit from `SynthefyError`:\n\n- `APITimeoutError`: Request timed out\n- `APIConnectionError`: Network/connection issues\n- `APIStatusError`: Base class for HTTP status errors\n - `BadRequestError` (400, 422): Invalid request data\n - `AuthenticationError` (401): Invalid API key\n - `PermissionDeniedError` (403): Access denied\n - `NotFoundError` (404): Resource not found\n - `RateLimitError` (429): Rate limit exceeded\n - `InternalServerError` (5xx): Server errors\n\nEach status error includes:\n- `status_code`: HTTP status code\n- `request_id`: Request ID for debugging (if available)\n- `error_code`: API-specific error code (if available)\n- `response_body`: Raw response body\n\n## Configuration\n\n### Environment Variables\n\n- `SYNTHEFY_API_KEY`: Your Synthefy API key\n\n## Support\n\nFor support and questions:\n- Email: contact@synthefy.com\n\n## License\n\nMIT License - see LICENSE file for details.",
"bugtrack_url": null,
"license": "MIT",
"summary": "Python client for the Synthefy API forecasting service",
"version": "2.0.4",
"project_urls": {
"Homepage": "https://synthefy.com"
},
"split_keywords": [
"api",
" forecasting",
" machine-learning",
" time-series"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "e014ae572577dae1f1e00fb498ea39822dafab8a0d150ba587dbb395c9f1e4e1",
"md5": "1098b83fca5e3f794fd26d6e2c9cfbc7",
"sha256": "9b745e9d47f2a9cfc2d30e5391834cf40f3b154e341a4eadcff897dcfa12b692"
},
"downloads": -1,
"filename": "synthefy-2.0.4-py3-none-any.whl",
"has_sig": false,
"md5_digest": "1098b83fca5e3f794fd26d6e2c9cfbc7",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 19350,
"upload_time": "2025-10-07T01:39:08",
"upload_time_iso_8601": "2025-10-07T01:39:08.170913Z",
"url": "https://files.pythonhosted.org/packages/e0/14/ae572577dae1f1e00fb498ea39822dafab8a0d150ba587dbb395c9f1e4e1/synthefy-2.0.4-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "c307a54985f20b84dcabd786b8961afd1c4552837ab7bdafda6662355e606440",
"md5": "19c235b4a6eb4257d8f65d0845c61405",
"sha256": "66ebc22ea9e8fbd7472ed0556355c9a9e0d0c6d7e21aac406d069d921c647f74"
},
"downloads": -1,
"filename": "synthefy-2.0.4.tar.gz",
"has_sig": false,
"md5_digest": "19c235b4a6eb4257d8f65d0845c61405",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 18166,
"upload_time": "2025-10-07T01:39:09",
"upload_time_iso_8601": "2025-10-07T01:39:09.341901Z",
"url": "https://files.pythonhosted.org/packages/c3/07/a54985f20b84dcabd786b8961afd1c4552837ab7bdafda6662355e606440/synthefy-2.0.4.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-07 01:39:09",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "synthefy"
}