Verlex documentation
Everything you need to run your code in the cloud with Verlex.
On this page
Quick Start
Get your API key
Grab your API key from the dashboard.
Run
Run your code on a cloud GPU:
import verlex
# Define your function
def train_model():
import torch
model = torch.nn.Linear(100, 10)
# Your training code here...
return {"accuracy": 0.95}
# Run it on a cloud GPU - that's it!
result = verlex.cloud(train_model, gpu="A100", api_key="gw_your_key")
print(result)Expected output: {'accuracy': 0.95}
Installation
Install Verlex using pip (requires Python 3.10+):
pip install verlexBasic Usage
Pass Your API Key
Every function accepts api_key directly, no context manager needed:
import verlex
# Run in the cloud, one line
result = verlex.cloud(my_function, api_key="gw_your_key")api_key and the resource keywords are keyword-only, so always write them as name=value. There is no separate "mode" argument or test environment: the gw_live_ and gw_test_ prefixes only name the key type; every key runs against the live service with live billing.
Passing Inputs
Your function's inputs come right after the function. A function that takes a single input accepts a bare value; a function that takes two or more inputs must receive them as one list:
# No inputs
verlex.cloud(train_model)
# One input, a bare value is fine
verlex.cloud(square, 5)
# Two or more inputs, pass them as a single list
verlex.cloud(train, [dataset, epochs])Specifying Resources
Every hardware field is optional, and only the fields you set are pinned. Whatever you leave out is sized from your code and chosen for cost and availability, so asking for a GPU and nothing else is the normal case:
# One GPU pinned, nothing else: cpu, memory and provider are chosen for you
result = verlex.cloud(train_model, [data], gpu="A100")
# Pin more when you need it — each field is independent
result = verlex.cloud(train_model, [data], gpu="H100", gpu_count=8)
result = verlex.cloud(train_model, [data], cpu=4, memory="16 GB")
# Pin the cloud too
result = verlex.cloud(train_model, [data], gpu="H100", provider="runpod")Passing the same machine around, or building it up in code? Wrap it in a Hardware object — identical meaning, one value:
from verlex import Hardware
big = Hardware(gpu="H100", gpu_count=8, memory="512 GB")
result = verlex.cloud(train_model, [data], big) # positionally
result = verlex.cloud(train_model, [data], hardware=big) # or by name
# Same fields, all optional — Hardware(gpu="A100") pins only the GPU
Hardware(gpu="A100").pinned() # {'gpu': 'A100'}Give a list to offer alternatives; Verlex takes the cheapest one available. Pinning a single provider disables cross-cloud failover for that job:
# Whichever of these is cheapest and available
result = verlex.cloud(train_model, [data], gpu=["H100", "A100"])
# Same for providers: a list lets Verlex choose, a single name pins hard
result = verlex.cloud(train_model, [data], gpu="A100", provider=["runpod", "gcp"])Providers you can pin: aws, gcp, azure, verda, runpod, vast, jarvislabs, hyperstack, tensordock, lyceum, beam, northflank, novita, cerebrium.
Values are checked in your process, before anything is provisioned: a misspelled option answers "did you mean gpu=?", and gpu="64 GB" tells you it belongs in memory=. A Hardware object and a keyword that disagree about the same field raise rather than one silently winning.
The full set of job options:
result = verlex.cloud(
train_model,
[data],
gpu="A100", # GPU type
cpu=8, # vCPU cores
memory="64GB", # memory
provider="aws", # pin to a cloud (omit to let Verlex pick the cheapest)
timeout=7200, # 2 hour timeout
pip_packages=["numpy==1.26.4"], # extra packages, always installed
python_version="3.11", # match your local Python
)Execution Modes
One flag controls your price-speed tradeoff:
Performance fast=True
Immediate execution
Best for time-sensitive workloads
Standard fast=False
Up to 10 min wait, lower price
Best for batch jobs, cost-sensitive work
# Performance mode, immediate execution
result = verlex.cloud(my_function, api_key="gw_your_key", fast=True)
# Standard mode (default), wait for the lowest price
result = verlex.cloud(my_function, api_key="gw_your_key")Performance mode (fast=True) requires the Performance plan ($10/mo). Standard mode (fast=False) is available on every plan.
Pricing & Billing
Two parts: a monthly plan, then a per-job cost. Verlex passes the provider's live hardware price through at cost and adds a fixed service fee per GPU-hour, prorated per second. There is no percentage markup on the hardware. The Standard plan is free forever ($0/month); the Performance plan ($10/month) unlocks fast mode and 300 GB of storage.
The service-fee tier is derived from the hardware's FP16 tensor performance (TFLOPS):
| Tier | Example hardware | Standard (fast=False) | Performance (fast=True) |
|---|---|---|---|
| CPU (CPU-only jobs) | No GPU | $0.02/hr | $0.02/hr |
| Small (<185 TFLOPS) | T4, L4, A10, RTX 3090 | $0.10/GPU-hr | $0.20/GPU-hr |
| Mid (<600 TFLOPS) | A100, L40S, RTX 4090 | $0.30/GPU-hr | $0.45/GPU-hr |
| Large (<1500 TFLOPS) | H100, H200, MI300X | $0.40/GPU-hr | $0.60/GPU-hr |
| Flagship (≥1500 TFLOPS) | B200, B300, GB200 | $0.50/GPU-hr | $0.75/GPU-hr |
The service fee is charged per GPU-hour. There is no cold-start surcharge; a cold machine and a warm one cost the same.
- Per-second billing, 1 second minimum: you pay only for the time your job actually runs. Both the provider cost and the service fee are prorated to the second.
- Prepaid credits: buy credits in the dashboard (minimum top-up $10). Each job places a hold and settles the exact amount when it finishes.
- Auto top-up (opt-in): adds credits automatically with your saved card when the balance runs low (default: $50 added when it drops below $10).
- Funds-based execution: a job runs until your credits are exhausted, it stops making progress (idle CPU/GPU with no output for a sustained period), or it finishes, with a hard 7-day backstop. The optional timeout argument is a client-side wait bound; when it expires, the client cancels the remote job.
Providers, Failover & Serverless
Every job is priced across 10 clouds (AWS, GCP, Azure, Verda, RunPod, Vast.ai, JarvisLabs, Hyperstack, TensorDock, Lyceum) plus serverless container lanes for small jobs, and routed to the cheapest machine that fits.
import verlex
# Default: priced across 10 clouds, routed to the cheapest machine that fits
result = verlex.cloud(train_model, gpu="A100", api_key="gw_your_key")
# Provider list: cost comparison and failover stay inside your subset
result = verlex.cloud(
train_model,
gpu="A100",
provider=["runpod", "gcp", "verda"],
api_key="gw_your_key",
)
# Hard pin: one provider, no cross-provider failover if it is out of stock
result = verlex.cloud(train_model, gpu="A100", provider="aws", api_key="gw_your_key")Failover: if a launch fails or a region is out of stock, Verlex fails over automatically, always cheapest first:
- 01Cheapest provider, primary region
- 02Nearby regions on the same provider
- 03Next cheapest cloud
- 04Substitute GPU (unless pinned)
Your job does not fail because one cloud ran out of capacity.
Pinning: a single pinned provider disables cross-provider failover: if that cloud has no capacity, the job fails instead of moving. Prefer a provider list, which keeps cost comparison and failover inside your chosen subset.
Serverless: small jobs are routed to serverless container lanes (billed per second) when that is cheaper than a VM, or faster within budget on the Performance plan. This happens automatically; nothing to configure.
Files & Workspace Sync
Send local files up to the VM before your job runs and bring generated files back when it finishes. Pass a Workspace to any cloud call. Auto mode mirrors your project root (respecting .gitignore) and returns whatever your code creates or changes; or list explicit Upload and Output specs when you want full control over what goes up and what comes back.
import verlex
from verlex import Workspace
def train():
from pathlib import Path
Path("runs").mkdir(exist_ok=True)
Path("runs/loss.json").write_text('{"loss": 0.42}')
Path("model.pt").write_text("weights")
return {"status": "done"}
# Auto mode mirrors your project root (respecting .gitignore) up to the VM,
# then returns new and changed files to the same paths locally
result = verlex.cloud(train, workspace=Workspace(), gpu="A100", api_key="gw_your_key")
# ./runs/loss.json and ./model.pt now exist locallyimport verlex
from verlex import Workspace, Upload, Output
def finetune():
import json
from pathlib import Path
cfg = json.load(open("config.json")) # uploaded and renamed below
Path("runs").mkdir(exist_ok=True)
Path("runs/epoch1.ckpt").write_text("weights") # trained on ./dataset
Path("model.pt").write_text("final weights")
return cfg["name"]
ws = Workspace(
root=None, # implied once uploads are listed; spelled out here for clarity
uploads=[
Upload("./configs/run.json", to="config.json"), # rename into the workspace
Upload("./dataset"), # whole folder, hierarchy kept
],
outputs=[
Output(path="model.pt"), # bring a single file back
Output(pattern="runs/*.ckpt"), # plus files matched by glob
],
)
result = verlex.cloud(finetune, workspace=ws, gpu="A100", api_key="gw_your_key")Your function runs inside the synced workspace, so relative paths like open('data/train.csv') work unchanged. Listing explicit Upload specs replaces the auto mirror — you get exactly the files you named, and nothing else; pass root='auto' if you want the project mirror alongside them. Explicit Output specs never affect what goes up. There is no hard size limit by default: Verlex warns once the workspace passes 100 MB, so set a ceiling with Workspace(max_size_bytes=...) if you need one. Import Workspace, Upload, and Output from the top-level verlex package.
Dependencies
Verlex scans your function's source, pins the versions of imported packages installed locally, and bundles local .py modules automatically. When you need certainty, pass pip_packages=: those packages are authoritative, always installed, and a failed install fails the job loudly instead of continuing silently.
import verlex
def train():
import numpy as np
import torch
from my_helpers import preprocess # local .py modules are bundled automatically
# ... your training code ...
return {"loss": 0.03}
# Auto-detect: imported packages are pinned to your local versions
result = verlex.cloud(train, api_key="gw_your_key")
# Authoritative installs: always installed, a failed install fails the job
result = verlex.cloud(
train,
api_key="gw_your_key",
pip_packages=["torch==2.3.1", "numpy==1.26.4"],
python_version="3.11", # 3.10, 3.11, 3.12, or 3.13
)python_version controls the interpreter on the cloud side: supported values are 3.10, 3.11, 3.12, and 3.13. By default Verlex matches your local interpreter.
Pre-warming
Kill the cold start before it happens. Call verlex.prewarm(your_function) and Verlex analyzes it, boots the right hardware, and hands you back a handle once the machine is ready (pass wait=False to get the handle immediately and let it boot in the background while your code runs). Calling the handle runs your function on that machine with no provisioning wait, and the machine stays attached to the handle between calls, so every call is warm. Release it with handle.release() or a with block when you are done; unreleased handles release themselves when your program exits, and a hard kill is covered by the server-side idle timeout.
New in 0.13.0: prewarm() replaces the old repo-scanning pre_warm(), which was removed.
import verlex
def train(batch):
import torch
...
wf = verlex.prewarm(train, gpu="A100", api_key="gw_your_key") # A100 starts booting NOW
data = load_and_clean() # local preprocessing runs meanwhile
result = wf(data) # runs train(data) on the warm machine
more = wf(other_data) # still warm, the VM stays attached
# Define inputs/outputs with a Workspace, just like verlex.cloud —
# it's a per-call argument on the warm handle:
from verlex import Workspace, Upload, Output
out = wf(data, workspace=Workspace(
uploads=[Upload("./configs/run.json", to="config.json")], # sync inputs up
outputs=[Output(pattern="checkpoints/*.ckpt")], # bring results back
))
wf.release() # recommended, not required: frees the machine now.
# If you skip it, the handle auto-releases when your
# program exits (atexit). A hard kill that skips atexit
# is covered by the server-side idle timeout instead.
# Or auto-release with a context manager:
with verlex.prewarm(train, "A100") as wf:
result = wf(data)
# Scope mode: prewarm() with no function sizes hardware from THIS file's
# code and binds no function. You pass the function to run as the first
# argument on every call (workspace= stays a keyword, so it comes last):
wf = verlex.prewarm(gpu="A10") # no function -> scope mode
result = wf(train, data) # function is the first call arg
out = wf(train, data, workspace=Workspace()) # same, with a Workspace
wf.release()There are two ways to call prewarm, and they change how you invoke the handle:
| Bound mode | Scope mode | |
|---|---|---|
| How you call it | verlex.prewarm(train, ...) | verlex.prewarm() with no function |
| What the handle binds | A specific function, pinned to the warm VM. | No function; the calling file's code is analyzed to size hardware. |
| Invocation | wf(data) just its arguments | wf(train, data) the function to run is the first argument of every call |
| Where hardware comes from | The kwargs you pass (gpu=, cpu=, memory=, provider=) or the analysis of the bound function. | The same kwargs or the calling-file analysis. |
| Workspace argument | wf(data, workspace=Workspace()) | wf(train, data, workspace=Workspace()) the function stays first |
- workspace= works the same as on any other cloud call: it syncs inputs up before the function runs and brings generated outputs back. Because it is a keyword, its position never changes, and it is per-call, so different calls on the same warm VM can carry different Workspaces.
- Multi-GPU cluster jobs, spot jobs, and workloads that resolve to the serverless lane skip pre-warming; they are already routed optimally.
- If the warm session expires before you call, the handle still works and simply provisions normally.
Billing: you pay only for the time the warm machine actually exists at the real instance rate, never for analysis time, failed warm-ups, or skipped sessions. Idle time between calls is billed in segments; job runtime is billed as a normal job.
The machine is released by whichever of these comes first:
| Trigger | When |
|---|---|
| Idle timeout | After 10 idle minutes, counted from the moment the machine is ready; each call or status check resets the clock. |
| Lifetime cap | At 1 hour of total session lifetime. |
| Manual release | As soon as you call release() (or the with block exits). |
| Running job | Never mid-job: a job already running on the machine is not interrupted by these timers. |
Automatic Cloud Offloading
Don't want to manage when code runs in the cloud? Let Verlex decide automatically. When your system's CPU, memory, or GPU usage exceeds the threshold, heavy functions are transparently offloaded to the cheapest cloud provider.
import verlex
# Pass your API key directly
verlex.overflow(api_key="gw_your_key")
# Your code runs normally.
# When CPU, memory, or GPU exceeds 85%, functions go to the cloud.
data = load_data()
result = train_model(data) # system overloaded? → cloud
evaluate(result) # resources free → runs locallyParameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| api_key | str | None | None | Your Verlex API key. |
| fast | bool | False | True = Performance mode (immediate execution, premium pricing). False = Standard mode. |
| threshold | float | 85.0 | CPU / memory usage percentage that triggers cloud offloading. Also used as the GPU threshold if gpu_threshold is not set. |
| gpu_threshold | float | None | None | GPU utilization or memory % that triggers cloud offloading. Defaults to threshold if not set. Requires pynvml and an NVIDIA GPU. |
| check_interval | float | 0.5 | Seconds between CPU/memory/GPU samples. |
| api_url | str | None | None | Override the Verlex API URL. |
| verbose | bool | True | Print offload activity and status to the console. |
Defaults: With just an API key, overflow monitors CPU, memory, and GPU. If any of them exceeds 85% usage, functions are automatically offloaded to the cloud in standard mode.
Note: GPU monitoring uses pynvml (included with the base install) and needs an NVIDIA GPU. Without one, overflow gracefully falls back to CPU and memory monitoring only.
CPU and memory monitoring need psutil. Install with: pip install 'verlex[overflow]'
AI assistants (MCP)
Connect Claude, Codex, Gemini CLI — or any MCP-compatible assistant — to your Verlex account and drive it in plain language: "run this fine-tune on an A100, show me the log as it goes, and kill it if the loss blows up".
Your assistant runs the connector locally and it talks to Verlex with your Verlex API key. Verlex never receives your Anthropic, OpenAI or Google credentials, and never sees your prompts.
Install
pip install "verlex[mcp]>=0.17.0"
# Storing your key once here means the configs below
# can carry no secret at all.
verlex loginConnect your assistant
Claude Code
claude mcp add verlex -- verlex-mcp
# View only: no submitting, cancelling or spending
claude mcp add verlex -- verlex-mcp --read-onlyClaude Desktop
Add this to claude_desktop_config.json — %APPDATA%\Claude\ on Windows, ~/Library/Application Support/Claude/ on macOS, ~/.config/Claude/ on Linux.
{
"mcpServers": {
"verlex": {
"command": "verlex-mcp",
"env": { "VERLEX_MCP_MAX_SPEND": "25" }
}
}
}Codex
[mcp_servers.verlex]
command = "verlex-mcp"
env = { VERLEX_MCP_MAX_SPEND = "25" }Gemini CLI
{
"mcpServers": {
"verlex": {
"command": "verlex-mcp",
"timeout": 60000
}
}
}Keep any client request timeout at 60 seconds or more: log tailing holds a request open for up to 25 seconds while it waits for new output.
What you can ask
Once connected, your assistant can do all of this on its own:
- →"What GPUs can I get, and what would an A100 cost me for two hours?"
- →"Run this script on an A100 and show me the output as it goes."
- →"Cancel that job if you see the loss go to NaN."
- →"List my last 10 jobs and what each one cost."
- →"How much have I spent this month, and how much credit is left?"
What it can reach
Ten tools: identity, GPU catalog and live pricing, cost estimates, script submission, job list and status, live log tailing, cancellation, usage summaries and billing. Each run is an independent job on a fresh machine — persistent nodes and file transfer are coming next.
Safety
The connector is a thin client holding your own API key, so it can only ever see and do what you can. There is no database access and no shared credential. On top of that:
- Anything that spends money or cancels work needs an explicit confirmation step, so it always surfaces as a visible action rather than a silent side effect.
- Every submission is priced first, and the estimate comes back with the job so you see the cost up front.
- A per-session spend cap (default $25), a one-GPU limit and a 60-minute duration limit are enforced before any request is sent.
- Job output is fenced and labelled as untrusted data, so instructions hidden inside a log cannot redirect your assistant.
Read-only mode
Add --read-only to the command and only the viewing tools are registered — nothing can be submitted, cancelled or spent. For a guarantee enforced by Verlex itself rather than by the connector, create the key with the jobs:read scope.
Authentication
Get Your API Key
Sign up at verlex.dev to get your API key.
Using Your API Key
import verlex
# Pass api_key directly to any function
result = verlex.cloud(my_function, api_key="gw_your_key")
# Or use environment variable (VERLEX_API_KEY)
result = verlex.cloud(my_function) # picks up from envError Handling
Verlex raises specific exceptions so you can handle failures gracefully:
from verlex.errors import (
VerlexError, # Base class for all Verlex errors
AuthenticationError, # Invalid or missing credentials
InsufficientCreditsError, # Not enough credits to run the job
ProviderMaintenanceError, # Pinned provider is under maintenance
JobFailedError, # Job execution failed in the cloud
JobTimeoutError, # Job exceeded its timeout
SerializationError, # Function could not be serialized
NetworkError, # Connection to Verlex API failed
RateLimitError, # Too many requests
)
import verlex
try:
result = verlex.cloud(train_model, api_key="gw_your_key", gpu="A100")
except InsufficientCreditsError as e:
print(f"Need more credits: {e.required} required, {e.available} available")
except JobFailedError as e:
print(f"Job {e.job_id} failed in the cloud. Logs:\n{e.logs}")
except ProviderMaintenanceError:
print("That provider is under maintenance. Pick another or drop the pin")
except JobTimeoutError:
print("Job took too long. Increase timeout or optimize your code")
except VerlexError as e:
print(f"Something went wrong: {e}")Available GPUs
Request a GPU by name with gpu="H100", or as Hardware(gpu="H100"). Verlex finds the cheapest available instance across 10 clouds, including AWS, GCP, Azure, Verda, RunPod, Vast.ai, JarvisLabs, Hyperstack, TensorDock, and Lyceum, plus serverless container lanes for small jobs.
| GPU | Memory | Best for |
|---|---|---|
| T4 | 16 GB | Inference, small training, budget-friendly |
| L4 | 24 GB | Inference, fine-tuning, video processing |
| A10 | 24 GB | Mixed inference and training |
| V100 | 16 GB | General ML training |
| A100 | 40/80 GB | Large model training, LLM fine-tuning |
| L40S | 48 GB | Large-scale inference and training |
| H100 | 80 GB | Largest models, fastest training |
| H200 | 141 GB | Maximum memory, cutting-edge workloads |
| B200 | 192 GB | Next-gen Blackwell, highest throughput |
| B300 | 288 GB | Blackwell Ultra, largest memory and FP4 throughput |
| RTX 3090 / 4090 / 5090 | 24-32 GB | Budget inference and fine-tuning (consumer cards) |
If you don't specify a GPU, Verlex auto-detects your code's needs and picks the best option. Availability and pricing vary by provider. Verlex always routes to the cheapest option.
Storage & Quotas
Job inputs, outputs, workspace files, and checkpoints live in Verlex object storage and count against your plan's storage quota:
Standard
25 GB of included storage.
Performance
300 GB of included storage.
Enterprise
1 TB+ of included storage: 1 TB by default, custom amounts on request.
Workspace upload blobs are stored by content hash and expire automatically after a period of non-use, so re-uploads of unchanged files are fast.
Checkpoints & Spot Recovery
Spot capacity is 60-80% cheaper than on-demand, but the provider can reclaim it at any time. Verlex handles the recovery: anything your job writes into the checkpoint directory is synced to object storage while it runs (every 60 seconds, every 20 seconds on short-notice providers, and immediately when a preemption notice arrives).
When a spot instance is reclaimed, the job is automatically re-enqueued on the next cheapest spot capacity (a different provider whenever one is stocked), the checkpoint directory is restored before your code runs again, and the reclaimed time is not billed. If spot keeps getting reclaimed, or no spot capacity fits the requested hardware anywhere, the job finishes on on-demand capacity so it always completes.
Write checkpoints, resume on restart
import os, pickle
def train():
# Set inside every Verlex job; synced to object storage while you run.
ckpt_dir = os.environ.get("GATEWAY_CHECKPOINT_DIR", "./ckpt")
state_file = os.path.join(ckpt_dir, "state.pkl")
start = 0
if os.path.exists(state_file):
with open(state_file, "rb") as f:
start = pickle.load(f)["epoch"]
for epoch in range(start, 100):
run_one_epoch()
tmp = state_file + ".tmp"
with open(tmp, "wb") as f:
pickle.dump({"epoch": epoch + 1}, f)
os.replace(tmp, state_file) # atomic: never syncs a torn fileFrameworks resume automatically
HuggingFace Trainer, PyTorch Lightning, and Keras jobs resume with no code changes: their checkpoint output is redirected into the synced directory and the latest checkpoint is picked up on restart. For plain Python state you can also use the injected helper: state = gw.resume() at the start, gw.checkpoint(epoch=epoch, total=total) each step.
The same restore also runs when an on-demand instance dies and the job is retried on fresh hardware. On providers with no advance reclaim notice, progress since the last periodic sync (at most the sync interval) can be lost.
Teams
Corporate accounts get organization-backed teams: a shared org credit pool, email invitations, and roles (owner, admin, member).
Admins allocate credits from the pool to members and can claw back unused allocations; members can also contribute personal credits to the pool. Everything is managed from the dashboard.
Spending Limits
Set daily or monthly spending caps from your dashboard to prevent unexpected charges. When a limit is reached, Verlex can block new jobs, warn you, or send a notification.
Block
New jobs are rejected until the next period or until you raise the limit.
Warn
Jobs still run, but you get a warning when approaching the limit.
Notify
You receive a notification when the limit is reached. Jobs continue.
Configure spending limits in your dashboard settings. You can also enable auto top-up to automatically add credits when your balance gets low.