Metadata-Version: 2.5
Name: symone
Version: 1.0.0
Summary: Official Python SDK for the SYMONE motorcycle transport API: search trips across France (Paris - Marseille and more). Standard library only, no API key.
Project-URL: Homepage, https://symone.fr/developers
Project-URL: Documentation, https://symone.fr/developers
Project-URL: API reference, https://symone.fr/api/index.md
Project-URL: OpenAPI, https://symone.fr/openapi.json
Project-URL: Book a trip, https://symone.fr/moto
Author-email: SYMONE SAS <contact@symone.fr>
Maintainer-email: SYMONE SAS <contact@symone.fr>
License-Expression: MIT
License-File: LICENSE
Keywords: ai-agents,api,france,marseille,moto,motorcycle,motorcycle-transport,paris,sdk,symone,transport,travel
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# symone

Official Python SDK for [SYMONE](https://symone.fr), long-distance motorcycle transport on French highways: your motorcycle rides on a trailer while you travel in the van. Main line Paris ⇄ Marseille, from 299 €.

- No account, no API key.
- Standard library only (`urllib`), Python 3.9+, typed dataclasses.
- Read-only: the API cannot book or pay. People book on [symone.fr/moto](https://symone.fr/moto), or ask for a quote for other routes and dates on [symone.fr/moto?tab=tab-devis](https://symone.fr/moto?tab=tab-devis).

Developer portal: <https://symone.fr/developers> · OpenAPI: <https://symone.fr/openapi.json>

## Install

```bash
pip install symone
```

## Quick start

```python
from symone import SymoneClient

client = SymoneClient()  # production: https://symone.fr/api-proxy/v1

page = client.search_routes(depart="Paris", arrival="Marseille", date_from="2026-11-01", date_to="2026-11-30")
for route in page.routes:
    print(route.date, route.depart, "->", route.arrival, route.layovers, route.price_label, route.slots, route.times)
print("Book on", page.book_url)
```

`Route` fields: `id`, `date` (YYYY-MM-DD), `depart`, `arrival`, `layovers`, `price_min` / `price_max` (EUR, `None` means on quote), `slots` (free motorcycle places for each leg), `times`. City filters are case-insensitive substrings and also match stopovers.

## Sandbox first

```python
client = SymoneClient(sandbox=True)  # https://symone.fr/api-proxy/v1/sandbox
page = client.search_routes(depart="Paris")
assert page.sandbox  # fixed sample trips dated 2030, never production data
```

## Pagination

```python
# Every trip, following next_cursor with the same filters
for route in client.iter_routes(depart="Marseille", page_size=100):
    ...

# Or page by page
page = client.search_routes(depart="Paris", limit=20)
if page.has_next:
    page = client.search_routes(depart="Paris", limit=20, cursor=page.next_cursor)
```

## Other endpoints

```python
client.cities_near("Paris, Île-de-France, France").cities

result = client.batch_search([                      # 1 to 10 queries, Idempotency-Key generated
    {"depart": "Paris", "arrival": "Marseille", "date_from": "2026-11-01"},
    {"depart": "Marseille", "arrival": "Paris"},
])
for item in result.results:
    print(item.index, item.total)

job = client.create_search_job(depart="Paris", arrival="Marseille")
done = client.wait_for_job(job, timeout=60)        # jobs expire after 10 minutes
print(done.routes)

answer = client.ask("Paris Marseille in November")  # NLWeb /ask, schema.org Trip objects
print(answer.answer, answer.results)

info = client.booking_info()                        # links and contacts, no network call
print(info.book_url, info.quote_url, info.email, info.phone_international)
```

Dates accept `datetime.date` objects or `"YYYY-MM-DD"` strings.

## Errors and rate limits

Limits are per client per minute: 180 reads, 30 actions (cities-near, batch, jobs, ask), 60 MCP calls. On HTTP 429 the client waits for `Retry-After` and retries once (`SymoneClient(retry_on_rate_limit=False)` disables it).

```python
from symone import SymoneError, APIError, NotFoundError, RateLimitError, TransportError

try:
    client.search_routes(depart="Paris")
except RateLimitError as error:
    print("retry in", error.retry_after, "s")
except APIError as error:          # JSON {"error": "..."} from the API
    print(error.status, error.message)
except TransportError as error:    # network failure or timeout
    print(error)
```

Hierarchy: `SymoneError` > `APIError` (> `NotFoundError`, `RateLimitError`), `TransportError`, `JobFailedError`, `JobTimeoutError`. Invalid arguments raise `ValueError` before any request.

## Command line

The package installs `symone-py`:

```bash
symone-py search --from Paris --to Marseille --date-from 2026-11-01
symone-py --sandbox search --from Paris --json
symone-py search --from Marseille --all
symone-py cities-near "Lyon"
symone-py batch queries.json
symone-py ask "Paris Marseille in November"
symone-py booking
```

Exit codes: 0 ok, 1 API error, 2 invalid usage, 3 rate limited, 4 network error. A Node.js CLI with more commands is available with `npx symone-cli`.

## MCP servers

For AI agents, SYMONE also runs two public MCP servers (Streamable HTTP, no auth): `https://symone.fr/mcp` (trip search) and `https://symone.fr/docs/mcp` (documentation).

## Links

- Developer portal: <https://symone.fr/developers>
- API reference: <https://symone.fr/api/index.md> · OpenAPI: <https://symone.fr/openapi.json>
- Versioning policy: <https://symone.fr/api/versioning.md>
- Book: <https://symone.fr/moto> · reservation@symone.fr · +33 1 71 32 35 05

## Development

```bash
python -m unittest   # no network: urllib is mocked
```

## License

MIT © SYMONE SAS
