Complete reference for the pm object available in Python scripts.
Python scripts use Pythonic naming (snake_case) and Postman
camelCase aliases — pm.collection_variables and
pm.collectionVariables are the same scope. The Pyodide and
RestrictedPython runtimes share this surface.
See Postman API parity for the full matrix and Snippets for the in-editor snippet palette.
Two Python runtimes:
pm_bootstrap.pyfor Pyodide (default when Deno + Pyodide assets are available)._py_sandbox.pyfor the RestrictedPython subprocess fallback. Both runtimes mirror each other'spm.*shape.
Read-only execution metadata.
| Property | Type | Description |
|---|---|---|
pm.info.request_name |
str |
Name of the current request |
pm.info.request_id |
str |
Database ID of the current request |
pm.info.iteration |
int |
Current iteration index (runner only) |
pm.info.iteration_count |
int |
Total iterations (runner only) |
The current HTTP request. Mutable in pre-request scripts only.
| Property | Type | Mutable | Description |
|---|---|---|---|
pm.request.url |
_PmUrl |
pre-request | Wrapped URL with getHost/getPath/getQueryString/protocol/host/port/path and a mutable query _HeaderList. String-coerces to the full URL. |
pm.request.method |
str |
pre-request | HTTP method. |
pm.request.headers |
_HeaderList |
pre-request | See below — case-insensitive get/has/find, dict-style [] sugar. |
pm.request.body |
_PmRequestBody |
pre-request | Discriminated union — mode/raw/urlencoded/formdata/graphql/file. |
# Pre-request: set auth header (dict-style sugar on _HeaderList).
pm.request.headers["Authorization"] = "Bearer " + pm.variables.get("token")
# Or the Postman-canonical form:
pm.request.headers.upsert({"key": "Authorization", "value": "Bearer " + pm.variables.get("token")})
# URL inspection / mutation.
print(pm.request.url.getHost()) # "api.example.com"
pm.request.url.query.add({"key": "page", "value": "2"})Same shape as the JS HeaderList. Used for pm.request.headers,
pm.response.headers, and body.urlencoded / body.formdata.
| Method | Description |
|---|---|
get(name) |
Case-insensitive value lookup. |
has(name) |
Case-insensitive presence check. |
find(name) |
{"key", "value"} dict (or None). |
idx(n) |
Entry at index n. |
each(fn) |
Iterate fn(entry) in insertion order. |
all() |
[{"key", "value"}, …]. |
to_object() / toObject() |
{key: value} dict. |
add({"key", "value"}) |
Append (mutable only). Response headers raise. |
remove(name) |
Remove all entries with that case-insensitive name. |
upsert({"key", "value"}) |
Update existing or append. |
headers["Name"] |
get(...) shortcut. |
headers["Name"] = "v" |
upsert(...) shortcut. |
name in headers |
has(...) shortcut. |
len(headers) / for h in … |
Length and iteration. |
| Member | Description |
|---|---|
mode |
"raw" | "urlencoded" | "formdata" | "graphql" | "file" | "". |
raw |
String body for mode == "raw". |
urlencoded |
Mutable _HeaderList. |
formdata |
Mutable _HeaderList. |
graphql |
{query, variables, operationName} dict (or None). |
file |
File spec dict (or None). |
if pm.request.body.mode == "urlencoded":
pm.request.body.urlencoded.upsert({"key": "page", "value": "2"})The HTTP response (None in pre-request scripts).
| Property | Type | Description |
|---|---|---|
pm.response.code |
int |
HTTP status code. |
pm.response.status |
str |
Status text from host (may differ from canonical reason — see reason()). |
pm.response.headers |
_HeaderList |
Response headers (read-only). |
pm.response.response_time |
float |
Elapsed time in ms. Also pm.response.responseTime (alias). |
pm.response.response_size |
int |
Response size in bytes. Also pm.response.responseSize (alias). |
pm.response.cookies |
_PmCookies |
Cookies parsed from this response's Set-Cookie headers. |
pm.response.originalRequest |
_PmRequest | None |
Wrapped (immutable) request that produced this response. |
| Method | Returns | Description |
|---|---|---|
json() |
dict | list |
Parse body as JSON. |
text() |
str |
Body as string. |
reason() |
str |
Canonical HTTP reason phrase for code (e.g. 200 → "OK"). |
mime() |
dict |
{"type", "charset"} parsed from Content-Type. |
dataURI() |
str |
Body encoded as data:<mime>;base64,<...>. |
size() |
int |
response_size if known, otherwise len(body). |
def check():
data = pm.response.json()
pm.expect(data).to.be.a("dict")
pm.test("JSON response", check)
def reason_check():
pm.expect(pm.response.reason()).to.equal("OK")
pm.test("Reason phrase", reason_check)Resolved scope — reads cascade across every scope; writes land in
local. Read precedence (highest first):
local → iteration_data → environment → collection_variables → globals.
This matches Postman's pm.variables semantics.
| Method | Returns | Description |
|---|---|---|
get(key) |
str | None |
First-match across scopes. |
set(key, value) |
— | Writes to local; subsequent reads see the override. |
has(key) |
bool |
Resolves through scopes. |
unset(key) |
— | Removes the local override only. |
clear() |
— | Empties the local layer (does not touch other scopes). |
to_object() / toObject() |
dict[str, Any] |
Merged view across all scopes (last-write-wins, local on top). |
replace_in(t) / replaceIn(t) |
str |
Substitute {{var}} patterns from the merged view. |
pm.variables.set("ts", datetime_now())
url = pm.variables.replace_in("{{base_url}}/users")Each scope is a _VariableScope exposing
get/set/has/unset/clear/to_dict/toObject/replace_in/replaceIn.
| Scope object | camelCase alias | Description |
|---|---|---|
pm.environment |
(same) | Environment-scoped variables. |
pm.collection_variables |
pm.collectionVariables |
Collection-scoped variables. |
pm.globals |
(same) | Global variables persisted to disk across all collections. |
pm.iteration_data |
pm.iterationData |
Read-only data row from the Collection Runner. |
Register a named test. fn may accept zero or one positional argument
— if it accepts one, the runtime passes a context object with a
.skip() method. Calling ctx.skip() short-circuits the body and
records the test as skipped.
pm.test("Status 200", lambda: pm.expect(pm.response.code).to.equal(200))
def check_body():
data = pm.response.json()
pm.expect(data).to.have.property("id")
pm.test("Has ID", check_body)
def maybe_check(ctx):
if not pm.environment.get("token"):
ctx.skip()
pm.expect(pm.environment.get("token")).to.have.length_of.above(0)
pm.test("Token shape", maybe_check)Record name as skipped without invoking fn. Useful for keeping a WIP
assertion in the file without it failing.
pm.test.skip("Body schema (WIP)", lambda: None)Create an assertion chain (Chai BDD-style with Python naming).
to, be, been, have, has_, at, of, same, deep.
not_ — inverts the next assertion.
pm.expect(404).not_.equal(200)| Method | Description |
|---|---|
.equal(val) |
Equality |
.eql(val) / .deep_equal(val) |
Deep equality (JSON comparison) |
.a(type_name) / .an(type_name) |
Type check ("string", "int", "list", etc.) |
.include(val) / .contain(val) |
Substring, element, or key |
.has_property(name, [val]) / .property(name, [val]) |
Dict key check |
.length_of(n) |
Length assertion |
.above(n) |
Greater than |
.below(n) |
Less than |
.least(n) |
>= comparison |
.most(n) |
<= comparison |
.match(pattern) |
Regex match (re.search) |
| `.status(code | reason)` |
.header(name, [val]) |
Response header check |
| `.body(string | Pattern)` |
.one_of([...]) / .oneOf([...]) |
Value is == to one of the items in the list. |
.json_body(path, [val]) / .jsonBody(...) |
Lodash-style path: a.b[0].c. |
| Property | Description |
|---|---|
.true |
Value is True |
.false |
Value is False |
.none |
Value is None |
.exist |
Value is not None |
.empty |
Length is 0 |
Cookie jar parsed from response Set-Cookie headers.
pm.response.cookies is an alias to the same object.
| Method | Returns | Description |
|---|---|---|
get(name) |
str | None |
Get cookie value by name. |
get_all() / getAll() |
list[dict] |
All cookies as [{"name", "value"}, …]. |
jar() |
_CookieJar |
See below. |
pm.test("Has session", lambda: pm.expect(pm.cookies.get("sid")).to.exist)Postman-style CookieJar. Reads work; mutators raise documented errors
pending host-side cookie storage.
| Method | Behaviour |
|---|---|
get(url, name[, callback]) |
Returns the cookie value parsed from this response's Set-Cookie. Optional callback fires. |
getAll(url[, callback]) |
Returns all known cookies for this response. |
set(...) |
Raises RuntimeError("pm.cookies.jar().set is not yet supported in postmark"). |
unset(...) |
Raises with the analogous error. |
clear(...) |
Raises with the analogous error. |
Send an HTTP sub-request. Synchronous — returns a wrapped _PmResponse
with the same shape as pm.response (so .code, .json(),
.headers.get(...), .text(), .reason(), .mime() etc. all work
identically).
No
await: pyodide and the RestrictedPython subprocess have no event loop. The call returns immediately with the response. Don't wrap it inawait— that's a JS-only path.
| Parameter | Type | Description |
|---|---|---|
spec |
str | dict |
URL string or request spec dict. |
callback |
callable | Optional fn(err, response) — fires synchronously with the wrapped _PmResponse. |
Spec dict fields: url, method, header/headers, body.
Limits: 10 calls per execution, http/https only, 10 s timeout.
resp = pm.send_request("https://api.example.com/token")
pm.environment.set("token", resp.json()["token"])
# Callback form (Postman parity):
def on_response(err, resp):
if not err:
pm.environment.set("token", resp.json()["token"])
pm.send_request("https://api.example.com/token", on_response)Runner flow control — available in both pre-request and test scripts. Both snake_case (Pythonic) and camelCase (Postman) names work.
| Member | Description |
|---|---|
set_next_request(name) / setNextRequest(name) |
Override the next request in the collection runner. |
skip_request() / skipRequest() |
Skip the current request's HTTP send. |
location.current |
Folder/collection path of the current request (string). |
set_next_request(name) sets the next request to execute by name.
Pass None to stop the runner after the current request. Only
effective inside the Collection Runner — ignored for single sends.
skip_request() prevents the HTTP request from being sent.
# Pre-request: conditionally skip
if pm.info.request_name.startswith("DRAFT"):
pm.execution.skip_request()
# Test: chain to another request
pm.test("Chain on 401", lambda: (
pm.execution.set_next_request("Login")
if pm.response.code == 401
else None
))Data-driven iteration row — populated when the Collection Runner is
configured with a CSV or JSON data file. The camelCase alias
pm.iterationData is the same object.
| Method | Returns | Description |
|---|---|---|
get(key) |
str | None |
Get value by column/key name. |
to_object() / toObject() |
dict |
All row data as a dict. |
has(key) |
bool |
Check if key exists in current row. |
user_id = pm.iteration_data.get("user_id")
pm.request.url = pm.variables.replace_in("{{base_url}}/users/") + user_idPostman-style module loader. Bare specifiers map to known vendor
names; otherwise falls through to importlib.import_module. Useful
when porting JS scripts that call pm.require("crypto-js") etc. —
reads succeed if the package is present in the sandbox; otherwise a
RuntimeError with the failed candidates list is raised.
moment = pm.require("moment") # if installed in pyodide
chai = pm.require("chai") # likewiseAlways raises:
pm.visualizer.set is not supported in postmark —
see data/snippets/README.md (Out of scope) for the rationale.
The explicit raise (rather than a no-op) makes it obvious to users that the call did nothing and prompts them to remove or replace it.
These exist as top-level names (no pm. prefix) for compatibility with
very old Postman scripts. New code should use the modern pm.* API.
| Global | Type | Maps to |
|---|---|---|
responseBody |
str |
pm.response.text() (or "" in pre-request). |
responseCode |
dict {"code", "name"} |
{"code": pm.response.code, "name": pm.response.reason()}. |
responseHeaders |
dict[str, str] |
pm.response.headers.to_object(). |
tests |
dict |
Postman v1 — assignments here are picked up by old runners. |
xml2Json(xml) |
function(str) -> dict | None |
XML → nested dict via stdlib xml.etree.ElementTree. |
postman |
object (see below) | Postman v1 helper namespace. |
| Method | Maps to |
|---|---|
setEnvironmentVariable(key, val) |
pm.environment.set(key, str(val)) |
getEnvironmentVariable(key) |
pm.environment.get(key) |
clearEnvironmentVariable(key) |
pm.environment.unset(key) |
setGlobalVariable(key, val) |
pm.globals.set(key, str(val)) |
getGlobalVariable(key) |
pm.globals.get(key) |
clearGlobalVariable(key) |
pm.globals.unset(key) |
setNextRequest(name) |
pm.execution.set_next_request(name) |
# Legacy v1 — works, but prefer the pm.* form below.
postman.setEnvironmentVariable("token", responseBody)
# Modern equivalent:
pm.environment.set("token", pm.response.text())Scripts cannot use import. Instead, pre-imported functions are
available as top-level names:
| Name | From | Description |
|---|---|---|
json_loads(s) |
json |
Parse JSON string |
json_dumps(obj) |
json |
Serialize to JSON string |
re_match(pattern, s) |
re |
Match at start |
re_search(pattern, s) |
re |
Search anywhere |
re_findall(pattern, s) |
re |
Find all matches |
re_sub(pattern, repl, s) |
re |
Regex replace |
math_ceil(x) |
math |
Ceiling |
math_floor(x) |
math |
Floor |
math_sqrt(x) |
math |
Square root |
math_pow(x, y) |
math |
Power |
math_log(x) |
math |
Natural log |
math_pi |
math |
Pi constant |
math_e |
math |
Euler's number |
b64encode(data) |
base64 |
Base64 encode |
b64decode(data) |
base64 |
Base64 decode |
hashlib_md5(data) |
hashlib |
MD5 hex digest |
hashlib_sha256(data) |
hashlib |
SHA-256 hex digest |
hashlib_hmac_sha256(data, key) |
hmac |
HMAC-SHA256 hex digest |
uuid_v4() |
uuid |
Random UUID v4 string |
datetime_now() |
datetime |
Current UTC ISO timestamp |
datetime_utcnow() |
datetime |
Alias for datetime_now() |
url_quote(s) |
urllib.parse |
URL-encode string |
url_urlencode(d) |
urllib.parse |
URL-encode dict |
print() output is captured and routed to the Console panel as
console.log messages. Rate-limited to 200 messages per execution.
print("Request URL:", pm.request.url)
print("Response:", pm.response.code)For Postman parity, common exception classes are exposed as builtins so
try/except works without imports:
Exception, ValueError, RuntimeError, KeyError, TypeError,
IndexError, AssertionError, AttributeError.
try:
data = pm.response.json()
except ValueError:
pm.test("Body is JSON", lambda: pm.expect(False).to.be.true)The following are blocked by RestrictedPython and the restricted builtins whitelist:
importstatementsopen(),exec(),eval(),__import__()getattr()on_-prefixed attributes- File I/O, network I/O, OS access
subprocess,os,sysmodule access