API Reference
Base URL: https://api.machinemarket.ai
All endpoints accept and return JSON. Payment serves as authentication for spawn and extend. Instance management (GET, DELETE) requires wallet ownership verification.
error string and optionally a code string for programmatic handling./v1/spawnProvision a new VPS instance. Requires a verified USDC payment on Base.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
tier | string | yes | "Nano" | "Small" | "Medium" | "Large" |
template | string | yes | "base" | "node" | "python" | "agent" | "openclaw" |
duration | string | yes | "1h" | "24h" | "7d" | "30d" |
region | string | no | Hetzner region ID. Defaults to "fsn1". See GET /v1/regions. |
tx_hash | string | yes | Transaction hash of the USDC payment on Base (0x-prefixed, 64 hex chars). |
wallet_address | string | yes | Sender wallet address (0x-prefixed, 40 hex chars). |
ssh_pubkey | string | no | Your SSH public key. If omitted, a keypair is generated and the private key is returned. |
Response (201)
| Field | Type | Required | Description |
|---|---|---|---|
instance.id | string | yes | UUID of the created instance. |
instance.tier | string | yes | The tier name. |
instance.template | string | yes | The template ID. |
instance.region | string | yes | Hetzner region ID. |
instance.status | string | yes | "provisioning" or "running". |
instance.ip_address | string | null | yes | IP address (null while provisioning). |
instance.created_at | string | yes | ISO 8601 timestamp. |
instance.expires_at | string | yes | ISO 8601 expiry timestamp. |
instance.cost_usdc | number | yes | Total cost in USDC. |
credentials.ssh_user | string | yes | Always "root". |
credentials.ssh_host | string | null | yes | Same as ip_address. |
credentials.ssh_port | number | yes | Always 22. |
credentials.ssh_private_key | string | no | PEM private key (only if ssh_pubkey was omitted). |
Error codes
| Field | Type | Required | Description |
|---|---|---|---|
400 | — | no | Invalid request body or parameters. |
402 | PAYMENT_INVALID | no | Payment verification failed (wrong amount, sender, or failed tx). |
409 | TX_DUPLICATE | no | Transaction hash already used. |
429 | WALLET_LIMIT | no | Max 10 concurrent instances per wallet. |
502 | PROVISION_FAILED | no | Hetzner provisioning failed. |
curl -X POST https://api.machinemarket.ai/v1/spawn \
-H "Content-Type: application/json" \
-d '{
"tier": "Small",
"template": "node",
"duration": "24h",
"tx_hash": "0xabc...def",
"wallet_address": "0x1234...5678"
}'const result = await mm.spawn({
tier: "Small",
template: "node",
duration: "24h",
tx_hash: "0xabc...def",
wallet_address: "0x1234...5678",
});/v1/instances/:idGet instance details. Requires the owner wallet address. If the instance is still provisioning, the API polls Hetzner for updated status.
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
wallet_address | string | yes | Owner wallet address (0x-prefixed, 40 hex chars). Must match the wallet that spawned the instance. |
Response (200)
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Instance UUID. |
tier | string | yes | Tier name. |
template | string | yes | Template ID. |
region | string | yes | Region ID. |
status | string | yes | "provisioning" | "running" | "expired" | "destroyed". |
ip_address | string | null | yes | IP address. |
created_at | string | yes | ISO 8601. |
expires_at | string | yes | ISO 8601. |
destroyed_at | string | null | yes | ISO 8601 or null. |
cost_usdc | number | yes | Total cost. |
Error codes
| Field | Type | Required | Description |
|---|---|---|---|
400 | — | no | Missing wallet_address query parameter. |
404 | — | no | Instance not found or wallet does not own it. |
curl "https://api.machinemarket.ai/v1/instances/YOUR_INSTANCE_ID?wallet_address=0x1234...5678"const instance = await mm.getInstance("YOUR_INSTANCE_ID", {
wallet_address: "0x1234...5678",
});
console.log(instance.status, instance.ip_address);/v1/instances/:idImmediately destroy an instance. Requires a signed message proving wallet ownership. The server is deleted from Hetzner.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
wallet_address | string | yes | Owner wallet address (0x-prefixed, 40 hex chars). Must match the wallet that spawned the instance. |
signature | string | yes | Wallet signature of the message "machinemarket:delete:<instance_id>". Proves you own the wallet. |
Response (200)
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Instance UUID. |
status | string | yes | Always "destroyed". |
Error codes
| Field | Type | Required | Description |
|---|---|---|---|
400 | — | no | Missing wallet_address or signature. |
403 | INVALID_SIGNATURE | no | Signature does not match wallet. |
404 | — | no | Instance not found or wallet does not own it. |
409 | — | no | Instance already terminated. |
502 | — | no | Failed to delete server from provider. |
# Sign the message "machinemarket:delete:YOUR_INSTANCE_ID" with your wallet, then:
curl -X DELETE https://api.machinemarket.ai/v1/instances/YOUR_INSTANCE_ID \
-H "Content-Type: application/json" \
-d '{
"wallet_address": "0x1234...5678",
"signature": "0xabcd...ef01"
}'import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(PRIVATE_KEY);
const instanceId = "YOUR_INSTANCE_ID";
// Sign the ownership proof
const signature = await account.signMessage({
message: `machinemarket:delete:${instanceId}`,
});
const result = await mm.destroyInstance(instanceId, {
wallet_address: account.address,
signature,
});
console.log(result.status); // "destroyed"/v1/instances/:id/extendExtend an active instance with a new USDC payment. The paying wallet must match the wallet that spawned the instance. The new duration is added to the current expiry.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
duration | string | yes | "1h" | "24h" | "7d" | "30d". |
tx_hash | string | yes | Transaction hash of the extension payment. |
wallet_address | string | yes | Sender wallet address. Must match the wallet that spawned the instance. |
Response (200)
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Instance UUID. |
expires_at | string | yes | New ISO 8601 expiry. |
extension_cost_usdc | number | yes | Cost of this extension. |
total_cost_usdc | number | yes | Cumulative cost. |
Error codes
| Field | Type | Required | Description |
|---|---|---|---|
400 | — | no | Invalid parameters. |
402 | PAYMENT_INVALID | no | Payment verification failed. |
403 | NOT_OWNER | no | Wallet does not own this instance. |
404 | — | no | Instance not found. |
409 | TX_DUPLICATE | no | Transaction hash already used. |
409 | — | no | Can only extend active instances. |
curl -X POST https://api.machinemarket.ai/v1/instances/YOUR_ID/extend \
-H "Content-Type: application/json" \
-d '{
"duration": "24h",
"tx_hash": "0xdef...789",
"wallet_address": "0x1234...5678"
}'const result = await mm.extendInstance("YOUR_ID", {
duration: "24h",
tx_hash: "0xdef...789",
wallet_address: "0x1234...5678",
});
console.log("New expiry:", result.expires_at);/v1/pricingGet all available pricing tiers and durations.
Response (200)
| Field | Type | Required | Description |
|---|---|---|---|
tiers | PricingTier[] | yes | Array of tier objects with name, vcpu, ram, storage, hourly/daily/monthly rates. |
durations | Duration[] | yes | Array of duration objects with label, value, hours. |
currency | string | yes | Always "USDC". |
chain | string | yes | Always "Base". |
curl https://api.machinemarket.ai/v1/pricingconst pricing = await mm.getPricing();
pricing.tiers.forEach(t =>
console.log(`${t.name}: $${t.hourly}/hr`)
);/v1/regionsGet available deployment regions.
Response (200)
| Field | Type | Required | Description |
|---|---|---|---|
regions | RegionInfo[] | yes | Array of region objects with id, name, country, available. |
default | string | yes | Default region ID ("fsn1"). |
curl https://api.machinemarket.ai/v1/regionsconst regions = await mm.getRegions();
const available = regions.regions.filter(r => r.available);
console.log(available);