Jump to content

Pulsed Media Customer API

From Pulsed Media Wiki


A Pulsed Media API key returns every service on the account as JSON in one request: seedboxes, storage boxes and dedicated servers, each with status, next due date, billing cycle and price. Each seedbox and storage box also reports its username, login address and six usage figures, the same ones its service page shows (data sent, data received, CPU hours, disk used, disk quota and torrents loaded), so several boxes are checked with one request instead of one login per box. A separate order key lets a script order new services, and the API never returns a password.

At a glance

Task Request Needs
Read every service on the account GET https://pulsedmedia.com/remote/pmapi-feed.php A read key
Get a price quote, then order a new service POST https://pulsedmedia.com/remote/pmapi-order.php An order key
Create, replace or revoke the keys Client area: Services → API access The account owner

The key goes in a header: X-API-Key: YOUR_KEY, or Authorization: Bearer YOUR_KEY. Both endpoints answer in JSON over HTTPS.

Quick start

  1. Log in to the client area and open Services → API access. Active seedbox and storage box service pages also link it as API access in the help line, and the direct address is https://pulsedmedia.com/clients/index.php?m=pmapi.
  2. Click Create an API key. This read key cannot place orders; ordering uses a separate order key, described under ordering. The key is shown once, so copy it before leaving the page.
  3. Read your services:
curl -H "X-API-Key: YOUR_KEY" https://pulsedmedia.com/remote/pmapi-feed.php

Only the account owner can create, replace or revoke a key. Users added to the account, and staff logged in to the client area on the owner's behalf, see the page but not the key controls.

The services feed

GET (or HEAD) on the feed returns one JSON object. A shortened example:

{
    "client_id": 12345,
    "generated_at": "2026-10-22T04:00:00Z",
    "currency": "EUR",
    "services": [
        {
            "id": 1001,
            "product": "M1000 S",
            "group": "Shared Seedboxes",
            "domain": "server1.pulsedmedia.com",
            "status": "Active",
            "registered": "2025-01-10",
            "next_due": "2026-10-25",
            "billing_cycle": "Monthly",
            "recurring_amount": "6.99",
            "seedbox": {
                "username": "alice",
                "login_url": "https://server1.pulsedmedia.com/user-alice/",
                "permanent_address": "http://0123456789abcdef.mcx.fi/",
                "usage": {
                    "tracked_since": "2025-01-10",
                    "sent_bytes": 52000000000000,
                    "received_bytes": null,
                    "cpu_hours": 12.5,
                    "cpu_since": "2026-09-01",
                    "disk_used_bytes": 1500000000000,
                    "disk_quota_bytes": 2000000000000,
                    "torrents": 812
                }
            }
        }
    ]
}

Account fields

Field Type Meaning
client_id integer The account the key belongs to
generated_at string When the answer was built, UTC, ISO 8601
currency string Currency code of the prices, for example EUR
services array Every service on the account, in service id order; empty when there are none

Service fields

Field Type Meaning
id integer Service id, the same number the client area shows
product string Product name
group string or null Product group
domain string For seedboxes and storage boxes, the server's hostname; may be empty for other products
status string Pending, Active, Completed, Suspended, Terminated, Cancelled or Fraud
registered, next_due string or null Dates as YYYY-MM-DD; null when not set
billing_cycle string As the client area shows it: Monthly, Quarterly, Semi-Annually, Annually, Biennially, Triennially, One Time or Free Account
recurring_amount string Recurring price with two decimals, in the account currency
seedbox object Present on seedboxes and storage boxes that have a username

The feed lists terminated and cancelled services too. Filter on status when only live services matter.

Seedbox and storage box fields

Field Type Meaning
username string The service login
login_url string The web dashboard address
permanent_address string An address that follows the service when it moves to another server; see Permalinks on PMSS
usage object or null The latest recorded usage, the same figures the service page shows; null when none is recorded yet
usage field Type Meaning
tracked_since string First day of the recorded totals
sent_bytes integer Data sent since tracked_since
received_bytes integer or null Data received; null where the server did not record it
cpu_hours number or null CPU time since cpu_since, one decimal (for example 12.5); null when no valid figure exists
cpu_since string or null First day the CPU figure covers
disk_used_bytes integer Disk in use at the latest reading
disk_quota_bytes integer or null Disk quota at the latest reading
torrents integer or null Torrents loaded at the latest reading

cpu_hours is a decimal number. A client that decodes it as an integer will reject the whole answer, so decode it as a float. Responses carry Cache-Control: no-store.

Recipes

These use jq 1.6 or later, with the key in the PM_KEY environment variable. The first two skip services where the field they need is null; the third prints - for a missing due date.

Services due in the next seven days, or already overdue:

curl -s -H "X-API-Key: $PM_KEY" https://pulsedmedia.com/remote/pmapi-feed.php |
  jq -r '.services[] | select(.status == "Active" and .next_due != null)
         | select((.next_due | strptime("%Y-%m-%d") | mktime) <= now + 7*86400)
         | "\(.next_due)  \(.product)  \(.recurring_amount)"'

Disk use as a percentage of quota, per seedbox or storage box:

curl -s -H "X-API-Key: $PM_KEY" https://pulsedmedia.com/remote/pmapi-feed.php |
  jq -r '.services[] | select(.status == "Active" and .seedbox.usage.disk_quota_bytes != null)
         | "\(.seedbox.username)  \(.seedbox.usage.disk_used_bytes * 100 / .seedbox.usage.disk_quota_bytes | floor)%"'

Every service as one tab-separated line:

curl -s -H "X-API-Key: $PM_KEY" https://pulsedmedia.com/remote/pmapi-feed.php |
  jq -r '.services[] | [.id, .status, .product, (.next_due // "-")] | @tsv'

Ordering through the API

Ordering needs an order key: click Create an order key on the API access page. The order key can place orders but cannot read services, and the read key cannot place orders, so a script that only reads never needs a key that can spend money. A request without confirm returns a quote and what the product needs. The same request with "confirm": true places the order, so a script that already knows the product can skip the quote. Replace 123 below with the id of a product that products.json lists as available. As written, this request returns the quote; add "confirm":true to the JSON to place the order.

curl -s -H "X-API-Key: YOUR_ORDER_KEY" -H "Content-Type: application/json" \
  -d '{"pid":123,"billingcycle":"monthly","customfields":{"Username":"myname"},"accept_tos":true}' \
  https://pulsedmedia.com/remote/pmapi-order.php
Field Type Meaning
pid integer Product id: the id in products.json, one of the public data feeds
billingcycle string One of the product's billing_cycles keys in products.json, for example monthly
customfields object Field name to value, for example "Username": "myname" on seedboxes
configoptions object Option name to choice name, or true/false for yes/no options. Options left out keep the order form's default
accept_tos boolean true accepts the terms of service. When the order form requires acceptance, a confirmed order without it is refused; a quote does not need it
confirm boolean true places the order; left out or false returns only the quote
expect_first_payment string Optional. The quote's first_payment, for example "12.50". A confirm whose first payment is no longer this amount is refused with 409 and the new quote, and nothing is ordered
expect_currency string Optional. The quote's currency code, for example "EUR". A confirm in another currency is refused the same way

The quote answer carries quote (product, billing cycle, first_payment, currency, a link to the terms) and product_needs: the custom fields with whether each is required and its choices, and the configurable options with their choices and defaults. The confirmed order answers 201 with order_id, invoice_id, pay_url and the same quote.

  • New orders only, one product per request, for products listed on the order form and in stock. Products with a quantity option cannot be ordered this way.
  • The order goes through the same checks as the order form does for a logged-in customer, and the usual order and invoice emails go out.
  • The API takes no payment. As with every invoice, any credit on the account is applied first; the rest is paid from the invoice like any other order, and the payment method can be changed there.
  • At most 5 unpaid orders on the account and 5 orders in 24 hours, counting orders placed any way, and one order request per account at a time.
  • Unknown fields are refused, and the request body may be at most 8192 bytes.

Status codes

Errors come back as JSON with an error field.

Code Meaning
200 Feed answer, or an order quote
201 Order placed
400 Anything after ? in the URL; the body is not a JSON object, has a top-level field not listed above, lacks pid or billingcycle, has a value of the wrong type, or is over 8192 bytes
401 Key missing, malformed or unknown; or the account is not active, or the key's creator no longer owns it
403 The key cannot be used here: a read key sent to the order endpoint, or an order key sent to the feed
404 The product does not exist or cannot be ordered through the API
405 Wrong method: the feed takes GET and HEAD, ordering takes POST
409 Out of stock; or, on a confirm with expect_first_payment or expect_currency, the price or currency is no longer the one expected. Nothing is ordered, and the answer carries the new quote
422 The order cannot be placed as sent: a billing cycle the product does not offer, an option or custom field name the product does not have, a product with a quantity option, a missing required field, terms not accepted, or a refused order-form check. The answer lists the problems and product_needs
429 5 unpaid orders, 5 orders in 24 hours, or another order request from the account is still running
500 Temporarily unavailable. On the order endpoint, no order was placed
502 The order could not be placed. Check the account's invoices before trying again
503 The API, or ordering through it, is switched off, or the feed is temporarily unavailable

How the key is protected

  • An account has one read key and, if the owner creates one, one order key. Pulsed Media stores only a hash of each, so a lost key cannot be shown again, only replaced.
  • Replace the key and Replace the order key issue a new key of that kind, and the old one stops at once. Revoke the key and Revoke the order key stop it at once. Replacing or revoking one key never touches the other.
  • The key works only while the account is active and the user who created it still owns the account.
  • Changing the account password does not stop the key. After a suspected leak, replace or revoke it on the API access page.
  • The page shows when each key was created and when it was last used.
  • The key is read from a header only. A URL with anything after ? is refused, so the key never sits in a URL.
  • Use https://. A request to http:// is redirected, but by then the key has crossed the network unencrypted; replace a key sent that way.
  • The read key can only read, and the order key can only order. The order key places orders invoiced to the account and paid at once from any credit on it, within the limits above, so give it only to the tool that orders. A key created before 22 October 2026 with Allow this key to place orders ticked can both read and order until it is replaced or revoked; the page says so next to it, and its replacement is a read key.
  • Web pages cannot call the API directly from a visitor's browser, because cross-origin requests are not supported. Call it from a script or a server.

Tools that use it

PMSS Fleet Manager, a community-made dashboard, reads plan prices and renewal dates through the API key and shows them next to each box's disk and traffic. Pulsed Media did not write it and does not maintain it.

mcxKokko is Pulsed Media's open-source skill pack for OpenClaw and other AI agents that read AgentSkills folders. Its account skill lists services with the read key. Its pm-order skill quotes a service and orders it with the order key only after the owner approves the command on OpenClaw's approval card, and the server refuses the order if the price is not the one on the card.

Every Pulsed Media seedbox and storage box account comes with this API, and a box ordered later needs no new setup: it is in the next answer, under the same key. See the seedbox plans.

See also