meshapi 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,147 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script or a egg install,
31
+ # but libs writing to lib/ are also common.
32
+ # lib/
33
+
34
+ # Unit test / coverage reports
35
+ htmlcov/
36
+ .tox/
37
+ .nox/
38
+ .coverage
39
+ .coverage.*
40
+ .cache
41
+ nosetests.xml
42
+ coverage.xml
43
+ *.cover
44
+ *.py,cover
45
+ .hypothesis/
46
+ .pytest_cache/
47
+ cover/
48
+
49
+ # Translations
50
+ *.mo
51
+ *.pot
52
+
53
+ # Django stuff:
54
+ *.log
55
+ local_settings.py
56
+ db.sqlite3
57
+ db.sqlite3-journal
58
+
59
+ # Flask stuff:
60
+ instance/
61
+ .webassets-cache
62
+
63
+ # Scrapy stuff:
64
+ .scrapy
65
+
66
+ # Sphinx documentation
67
+ docs/_build/
68
+
69
+ # PyBuilder
70
+ .pybuilder/
71
+ target/
72
+
73
+ # Jupyter Notebook
74
+ .ipynb_checkpoints
75
+
76
+ # IPython
77
+ profile_default/
78
+ ipython_config.py
79
+
80
+ # pyenv
81
+ # For a library or binary, you shouldn't ignore the .python-version file,
82
+ # as it's used to specify the project's default python translation.
83
+ # .python-version
84
+
85
+ # pipenv
86
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
87
+ # However, in case of collaboration, if you are working on a python library it depends on you
88
+ # to decide if you want to include it or not.
89
+ #Pipfile.lock
90
+
91
+ # poetry
92
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
93
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
94
+ #poetry.lock
95
+
96
+ # pdm
97
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
98
+ #pdm.lock
99
+
100
+ # PEP 582; used by e.g. pdm
101
+ __pypackages__/
102
+
103
+ # Celery stuff
104
+ celerybeat-schedule
105
+ celerybeat.pid
106
+
107
+ # SageMath parsed files
108
+ *.sage.py
109
+
110
+ # Environments
111
+ .env
112
+ .venv
113
+ env/
114
+ venv/
115
+ ENV/
116
+ env.bak/
117
+ venv.bak/
118
+
119
+ # Spyder project settings
120
+ .spyderproject
121
+ .spyder-py3
122
+
123
+ # Rope project settings
124
+ .ropeproject
125
+
126
+ # mkdocs documentation
127
+ /site
128
+
129
+ # mypy
130
+ .mypy_cache/
131
+ .dmypy.json
132
+ dmypy.json
133
+
134
+ # Pyre type checker
135
+ .pyre/
136
+
137
+ # pytype static type analyzer
138
+ .pytype/
139
+
140
+ # Cython debug symbols
141
+ cython_debug/
142
+
143
+ # PyCharm
144
+ .idea/
145
+
146
+ # VS Code
147
+ .vscode/
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ ## [0.1.0] — Initial release
4
+
5
+ - `MeshAPI` (sync) and `AsyncMeshAPI` (async) via httpx
6
+ - Chat completions: non-streaming (`create`) and streaming (`stream`)
7
+ - Models: `list`, `free`, `paid`
8
+ - Templates: `create`, `list`, `get`, `update`, `delete`
9
+ - `MeshAPIError` with `status`, `error_code`, `request_id`, `details`, `retry_after_seconds`
10
+ - Retry with exponential backoff (default 3 retries, codes 429/502/503/504)
11
+ - SSE remainder-buffer parser for robust TCP fragmentation handling
12
+ - Streaming fail-fast: no automatic reconnect (documented)
13
+ - `X-MeshAPI-SDK: python/0.1.0` header on every request
meshapi-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,519 @@
1
+ Metadata-Version: 2.4
2
+ Name: meshapi
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for the MeshAPI AI model gateway
5
+ Project-URL: Homepage, https://meshapi.ai
6
+ Project-URL: Documentation, https://developers.meshapi.ai
7
+ Project-URL: Repository, https://github.com/aifiesta/meshapi-python-sdk
8
+ Project-URL: Issues, https://github.com/aifiesta/meshapi-python-sdk/issues
9
+ Project-URL: Changelog, https://github.com/aifiesta/meshapi-python-sdk/blob/main/CHANGELOG.md
10
+ Author-email: MeshAPI <contact@meshapi.ai>
11
+ License: MIT
12
+ Keywords: ai-gateway,anthropic,batches,embeddings,llm,meshapi,openai,openai-compatible,prompt-templates,sdk
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Classifier: Typing :: Typed
26
+ Requires-Python: >=3.9
27
+ Requires-Dist: httpx>=0.27.0
28
+ Requires-Dist: pydantic>=2.0.0
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
31
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
32
+ Requires-Dist: respx>=0.21.0; extra == 'dev'
33
+ Description-Content-Type: text/markdown
34
+
35
+ # meshapi
36
+
37
+ Official Python SDK for [Mesh API](https://meshapi.ai), an AI model gateway that gives you instant access to 300+ LLMs through a single OpenAI-compatible API.
38
+
39
+ Code once with the chat completions signature you already know. Switch between OpenAI, Anthropic, Google, Meta, Mistral, DeepSeek, xAI, Alibaba and the rest by changing a model string. Streaming, tool calling, vision, embeddings, multi-model compare, batch jobs and prompt templates from a single client.
40
+
41
+ ```python
42
+ from meshapi import MeshAPI, ChatCompletionParams, ChatMessage
43
+
44
+ client = MeshAPI(base_url="https://api.meshapi.ai", token="rsk_...")
45
+
46
+ reply = client.chat.completions.create(
47
+ ChatCompletionParams(
48
+ model="anthropic/claude-sonnet-4.5",
49
+ messages=[ChatMessage(role="user", content="Write a haiku about Python.")],
50
+ )
51
+ )
52
+
53
+ print(reply.choices[0].message.content)
54
+ ```
55
+
56
+ Python 3.9+. Built on `httpx` and Pydantic v2. Sync and async clients with first-class type hints.
57
+
58
+ ## Install
59
+
60
+ ```bash
61
+ pip install meshapi
62
+ # or
63
+ uv add meshapi
64
+ # or
65
+ poetry add meshapi
66
+ ```
67
+
68
+ Get a key at [meshapi.ai](https://meshapi.ai). Data-plane keys are prefixed `rsk_`.
69
+
70
+ ## What you get
71
+
72
+ | | |
73
+ |---|---|
74
+ | **One Universal API** | Code once. A single `chat.completions.create` call works across 300+ base models. |
75
+ | **Sync and async** | Pick `MeshAPI` for scripts, `AsyncMeshAPI` for servers. Same surface, same params. |
76
+ | **Streaming + tool calling** | SSE streaming via `Iterator` / `AsyncIterator`, function calling with structured tool definitions, vision and audio content parts. |
77
+ | **Reasoning models** | First-class `responses` API with `reasoning.effort` and `max_output_tokens` for o-series and similar models. |
78
+ | **Embeddings** | Drop-in OpenAI-compatible embeddings endpoint. |
79
+ | **Multi-model compare** | Fire one prompt at N models in parallel and stream their replies side by side. |
80
+ | **Batches + Files** | Async bulk inference jobs at discounted rates with file upload, download and lifecycle management. |
81
+ | **Prompt templates** | Server-stored prompts with `{{variable}}` slots. Update prompts without redeploying. |
82
+ | **Provider fallbacks** | If a provider experiences downtime, the gateway falls back to another supported model so your inference stays up. |
83
+ | **Built-in rate limiting** | Per-key RPM and RPD limits to prevent runaway costs. HTTP 429 with `retry_after` surfaced as `MeshAPIError.retry_after_seconds`. |
84
+ | **Unified billing** | One account balance covers every model. No juggling subscriptions. |
85
+ | **Structured errors** | `MeshAPIError` with `error_code`, `status`, `request_id`, `retry_after_seconds`, and provider error details. |
86
+ | **Type-safe** | Every request and response is a Pydantic v2 model. Autocomplete in your editor, validation at the boundary. |
87
+
88
+ ## Configuration
89
+
90
+ ```python
91
+ client = MeshAPI(
92
+ base_url="https://api.meshapi.ai", # required
93
+ token="rsk_...", # required: data-plane key or Supabase JWT
94
+ timeout=60.0, # default 60s
95
+ max_retries=3, # default 3, set 0 to disable
96
+ httpx_client=None, # optional: inject a custom httpx.Client
97
+ )
98
+ ```
99
+
100
+ `AsyncMeshAPI` takes the same arguments (with `async_httpx_client` instead of `httpx_client`) and supports `async with` for clean shutdown.
101
+
102
+ Two auth realms. Use one client per realm.
103
+
104
+ | Realm | Token | Resources |
105
+ |---|---|---|
106
+ | **Data-plane** | `rsk_<ULID>` | `chat`, `responses`, `embeddings`, `compare`, `files`, `batches` |
107
+ | **Control-plane** | Supabase JWT | `templates`, `models` |
108
+
109
+ `models` accepts either token type.
110
+
111
+ ## Chat completions
112
+
113
+ ```python
114
+ from meshapi import MeshAPI, ChatCompletionParams, ChatMessage
115
+
116
+ client = MeshAPI(base_url="https://api.meshapi.ai", token="rsk_...")
117
+
118
+ reply = client.chat.completions.create(
119
+ ChatCompletionParams(
120
+ model="openai/gpt-4o-mini",
121
+ messages=[
122
+ ChatMessage(role="system", content="You are a concise assistant."),
123
+ ChatMessage(role="user", content="What is the capital of France?"),
124
+ ],
125
+ temperature=0.7,
126
+ max_tokens=256,
127
+ )
128
+ )
129
+
130
+ print(reply.choices[0].message.content)
131
+ print(f"Tokens: {reply.usage.total_tokens}")
132
+ ```
133
+
134
+ ### Async
135
+
136
+ ```python
137
+ import asyncio
138
+ from meshapi import AsyncMeshAPI, ChatCompletionParams, ChatMessage
139
+
140
+ async def main():
141
+ async with AsyncMeshAPI(base_url="https://api.meshapi.ai", token="rsk_...") as client:
142
+ reply = await client.chat.completions.create(
143
+ ChatCompletionParams(
144
+ model="openai/gpt-4o-mini",
145
+ messages=[ChatMessage(role="user", content="Hello!")],
146
+ )
147
+ )
148
+ print(reply.choices[0].message.content)
149
+
150
+ asyncio.run(main())
151
+ ```
152
+
153
+ ### Streaming
154
+
155
+ `stream()` is a separate method from `create()`. It returns an iterator (sync) or async iterator (async) of chunks.
156
+
157
+ ```python
158
+ for chunk in client.chat.completions.stream(
159
+ ChatCompletionParams(
160
+ model="openai/gpt-4o-mini",
161
+ messages=[ChatMessage(role="user", content="Write a haiku about Python.")],
162
+ )
163
+ ):
164
+ if chunk.choices and chunk.choices[0].delta:
165
+ print(chunk.choices[0].delta.content or "", end="", flush=True)
166
+ ```
167
+
168
+ Async streaming:
169
+
170
+ ```python
171
+ async for chunk in client.chat.completions.stream(params):
172
+ if chunk.choices and chunk.choices[0].delta:
173
+ print(chunk.choices[0].delta.content or "", end="", flush=True)
174
+ ```
175
+
176
+ ### Tool calling
177
+
178
+ ```python
179
+ from meshapi import ChatCompletionParams, ChatMessage, Tool, ToolFunction
180
+
181
+ params = ChatCompletionParams(
182
+ model="openai/gpt-4o",
183
+ messages=[ChatMessage(role="user", content="What is the weather in Paris?")],
184
+ tools=[
185
+ Tool(
186
+ type="function",
187
+ function=ToolFunction(
188
+ name="get_weather",
189
+ description="Get current weather for a city",
190
+ parameters={
191
+ "type": "object",
192
+ "properties": {"city": {"type": "string"}},
193
+ "required": ["city"],
194
+ },
195
+ ),
196
+ )
197
+ ],
198
+ tool_choice="auto",
199
+ )
200
+
201
+ for chunk in client.chat.completions.stream(params):
202
+ delta = chunk.choices[0].delta if chunk.choices else None
203
+ if delta and delta.tool_calls:
204
+ print("tool call:", delta.tool_calls)
205
+ elif delta and delta.content:
206
+ print(delta.content, end="", flush=True)
207
+ ```
208
+
209
+ ## Responses API (reasoning models)
210
+
211
+ ```python
212
+ from meshapi import ResponsesParams
213
+
214
+ reply = client.responses.create(
215
+ ResponsesParams(
216
+ model="openai/o4-mini",
217
+ input="Explain the halting problem in two sentences.",
218
+ reasoning={"effort": "medium"},
219
+ max_output_tokens=512,
220
+ )
221
+ )
222
+
223
+ print(reply.output)
224
+ ```
225
+
226
+ Streaming works the same way via `client.responses.stream(params)`.
227
+
228
+ ## Embeddings
229
+
230
+ ```python
231
+ from meshapi import EmbeddingsParams
232
+
233
+ result = client.embeddings.create(
234
+ EmbeddingsParams(
235
+ model="openai/text-embedding-3-small",
236
+ input=["hello world", "goodbye world"],
237
+ )
238
+ )
239
+
240
+ print(len(result.data[0].embedding))
241
+ ```
242
+
243
+ ## Compare (multi-model fanout)
244
+
245
+ Fire one prompt at several models and stream their replies in parallel.
246
+
247
+ ```python
248
+ from meshapi import CompareParams, ChatMessage
249
+
250
+ stream = client.compare.stream(
251
+ CompareParams(
252
+ models=[
253
+ "openai/gpt-4o-mini",
254
+ "anthropic/claude-sonnet-4.5",
255
+ "google/gemini-2.5-flash",
256
+ ],
257
+ messages=[ChatMessage(role="user", content="Summarize this paragraph in one sentence: ...")],
258
+ stream=True,
259
+ )
260
+ )
261
+
262
+ for event in stream:
263
+ if event.event == "delta":
264
+ print(event.data)
265
+ ```
266
+
267
+ Use `client.compare.create(params)` for a non-streaming side-by-side response with an optional judge-model comparison via `comparison_model`.
268
+
269
+ ## Files and Batches
270
+
271
+ Upload a list of requests, kick off a batch, poll until done. Batch jobs run at discounted pricing.
272
+
273
+ ```python
274
+ from meshapi import (
275
+ UploadBatchFileParams,
276
+ BatchRequestItem,
277
+ CreateBatchParams,
278
+ )
279
+
280
+ # 1. Upload the batch input
281
+ file = client.files.upload(
282
+ UploadBatchFileParams(
283
+ purpose="batch",
284
+ requests=[
285
+ BatchRequestItem(
286
+ custom_id="req-1",
287
+ body={
288
+ "model": "openai/gpt-4o-mini",
289
+ "messages": [{"role": "user", "content": "Say hi."}],
290
+ },
291
+ ),
292
+ BatchRequestItem(
293
+ custom_id="req-2",
294
+ body={
295
+ "model": "openai/gpt-4o-mini",
296
+ "messages": [{"role": "user", "content": "Say bye."}],
297
+ },
298
+ ),
299
+ ],
300
+ )
301
+ )
302
+
303
+ # 2. Create the batch
304
+ batch = client.batches.create(
305
+ CreateBatchParams(
306
+ input_file_id=file.id,
307
+ endpoint="/v1/chat/completions",
308
+ completion_window="24h",
309
+ )
310
+ )
311
+
312
+ # 3. Poll later
313
+ status = client.batches.get(batch.id)
314
+ if status.status == "completed" and status.output_file_id:
315
+ output_bytes = client.files.content(status.output_file_id)
316
+ # output_bytes is JSONL
317
+ ```
318
+
319
+ ## Models
320
+
321
+ ```python
322
+ all_models = client.models.list()
323
+ free = client.models.free()
324
+ paid = client.models.paid()
325
+
326
+ for m in paid[:5]:
327
+ print(
328
+ f"{m.id}: prompt ${m.pricing.prompt_usd_per_1k}/1k, "
329
+ f"completion ${m.pricing.completion_usd_per_1k}/1k"
330
+ )
331
+ ```
332
+
333
+ Free models (`is_free=True`) cost $0 for both prompt and completion, useful for testing and light tasks. Paid models charge per token against your account balance.
334
+
335
+ ## Prompt templates
336
+
337
+ Server-stored prompts with `{{variable}}` interpolation. Reference them by name from `chat.completions` to skip re-sending system prompts every request.
338
+
339
+ ```python
340
+ from meshapi import MeshAPI, CreateTemplateParams, ChatCompletionParams, ChatMessage
341
+
342
+ # Manage templates with a control-plane JWT
343
+ ctrl = MeshAPI(base_url="https://api.meshapi.ai", token=supabase_session_jwt)
344
+
345
+ ctrl.templates.create(
346
+ CreateTemplateParams(
347
+ name="support-agent",
348
+ system="You are a support agent for {{company}}. Be concise and friendly.",
349
+ model="openai/gpt-4o-mini",
350
+ variables=["company"],
351
+ )
352
+ )
353
+
354
+ # Use the template with a data-plane rsk_ key
355
+ client = MeshAPI(base_url="https://api.meshapi.ai", token="rsk_...")
356
+
357
+ reply = client.chat.completions.create(
358
+ ChatCompletionParams(
359
+ messages=[ChatMessage(role="user", content="How do I reset my password?")],
360
+ template="support-agent",
361
+ variables={"company": "Acme Corp"},
362
+ )
363
+ )
364
+ ```
365
+
366
+ CRUD:
367
+
368
+ ```python
369
+ from meshapi import UpdateTemplateParams
370
+
371
+ templates = ctrl.templates.list()
372
+ t = ctrl.templates.get(template_id)
373
+ ctrl.templates.update(template_id, UpdateTemplateParams(model="openai/gpt-4o"))
374
+ ctrl.templates.delete(template_id)
375
+ ```
376
+
377
+ ## Error handling
378
+
379
+ ```python
380
+ from meshapi import MeshAPIError
381
+
382
+ try:
383
+ client.chat.completions.create(params)
384
+ except MeshAPIError as e:
385
+ print(f"[{e.status}] {e.error_code}: {e}")
386
+ print("Request ID:", e.request_id)
387
+
388
+ if e.error_code == "rate_limit_exceeded":
389
+ print(f"Retry after {e.retry_after_seconds}s")
390
+ elif e.error_code == "spend_limit_exceeded":
391
+ print("Account balance exhausted. Top up to continue.")
392
+ elif e.error_code == "unauthorized":
393
+ print("Invalid API key.")
394
+ elif e.error_code == "model_not_found":
395
+ print("Model not supported.")
396
+ elif e.error_code == "upstream_error":
397
+ print("Provider error:", e.provider_error)
398
+ elif e.error_code == "validation_error":
399
+ print("Invalid request:", e.details)
400
+ ```
401
+
402
+ | Code | HTTP | Meaning |
403
+ |---|---|---|
404
+ | `unauthorized` | 401 | Invalid or missing key |
405
+ | `forbidden` | 403 | Key suspended |
406
+ | `not_found` / `model_not_found` | 404 | Resource or model not found |
407
+ | `spend_limit_exceeded` | 402 | Account balance at zero. Top up to continue. |
408
+ | `validation_error` / `unprocessable_entity` | 422 | Bad request body |
409
+ | `rate_limit_exceeded` | 429 | RPM or RPD limit hit |
410
+ | `upstream_error` / `gateway_timeout` / `internal_error` | 500 | Upstream or server error |
411
+ | `parse_error` | n/a | SDK could not parse response body |
412
+ | `stream_interrupted` | n/a | Mid-stream connection dropped |
413
+
414
+ Mid-stream errors (sent as SSE frames before `[DONE]`) raise the same `MeshAPIError` from inside the iterator.
415
+
416
+ ## Retry and backoff
417
+
418
+ The client automatically retries `GET` and non-streaming `POST` / `PATCH` requests on status codes `429`, `502`, `503`, `504` with exponential backoff (default: 3 retries, base delay 500 ms, max 30 s, with jitter). The `Retry-After` header is respected on 429 responses.
419
+
420
+ ```python
421
+ client = MeshAPI(
422
+ base_url="https://api.meshapi.ai",
423
+ token="rsk_...",
424
+ max_retries=5, # 0 to disable
425
+ timeout=30.0,
426
+ )
427
+ ```
428
+
429
+ ## Streaming failure recovery
430
+
431
+ **Streams do not retry.** If a connection drops mid-stream, a `MeshAPIError` with `error_code="stream_interrupted"` is raised. Catch it and restart a new request:
432
+
433
+ ```python
434
+ try:
435
+ for chunk in client.chat.completions.stream(params):
436
+ process(chunk)
437
+ except MeshAPIError as e:
438
+ if e.error_code == "stream_interrupted":
439
+ # restart from scratch
440
+ ...
441
+ ```
442
+
443
+ ## Type hints
444
+
445
+ Every request and response is a Pydantic v2 model. Import what you need:
446
+
447
+ ```python
448
+ from meshapi import (
449
+ MeshAPI,
450
+ AsyncMeshAPI,
451
+ MeshAPIConfig,
452
+ MeshAPIError,
453
+ # chat
454
+ ChatCompletionParams,
455
+ ChatCompletionResponse,
456
+ ChatCompletionChunk,
457
+ ChatMessage,
458
+ Tool,
459
+ ToolFunction,
460
+ ToolCall,
461
+ # responses
462
+ ResponsesParams,
463
+ ResponsesResponse,
464
+ # embeddings
465
+ EmbeddingsParams,
466
+ EmbeddingsResponse,
467
+ # compare
468
+ CompareParams,
469
+ CompareStreamEvent,
470
+ # batches + files
471
+ UploadBatchFileParams,
472
+ BatchRequestItem,
473
+ CreateBatchParams,
474
+ BatchObject,
475
+ FileObject,
476
+ # models
477
+ ModelInfo,
478
+ ModelPricing,
479
+ # templates
480
+ CreateTemplateParams,
481
+ UpdateTemplateParams,
482
+ TemplateSummary,
483
+ )
484
+ ```
485
+
486
+ ## Versioning
487
+
488
+ This SDK follows [SemVer 2.0](https://semver.org/). Pre-1.0 releases may have breaking changes between minor versions.
489
+
490
+ ```python
491
+ import meshapi
492
+ print(meshapi.__version__) # "0.1.0"
493
+ ```
494
+
495
+ ## About Mesh API
496
+
497
+ [Mesh API](https://meshapi.ai) is an AI model gateway that gives you instant access to a massive variety of LLMs through a single, unified API. Enjoy the developer experience you already know, upgraded with universal model access.
498
+
499
+ | | |
500
+ |---|---|
501
+ | **One Universal API** | A single `chat.completions.create` request works across 300+ base models. |
502
+ | **Unified Billing** | Deposit funds into one account and consume any model. No juggling provider subscriptions. |
503
+ | **Free Tier** | Free models (`is_free=True`) cost $0 for both prompt and completion. Test and ship light workloads without funding. |
504
+ | **Provider Fallbacks** | If a model or provider goes down, the gateway routes to another supported model so your inference stays up. |
505
+ | **Built-in Rate Limiting** | Robust per-key limits prevent runaway costs. |
506
+ | **Prompt Templates** | Manage, version and share prompts via a secure templating system. |
507
+
508
+ Documentation lives at [developers.meshapi.ai](https://developers.meshapi.ai).
509
+
510
+ Built by the founders of [TagMango](https://tagmango.com) (YC W20) and [AI Fiesta](https://aifiesta.ai) (1M+ users).
511
+
512
+ ## Related
513
+
514
+ - [`meshapi-node-sdk`](https://github.com/aifiesta/meshapi-node-sdk): official TypeScript SDK with the same surface
515
+ - [`meshapi-code`](https://github.com/aifiesta/meshapi-code): terminal chat REPL with tool calling
516
+
517
+ ## License
518
+
519
+ [MIT](LICENSE)