20,919,685Total Points24,106Total Sites23,382Cell Towers69,500Racks Monitored56,753HVAC Units Monitored31,322UPS Units Monitored24,788Generators Monitored20,919,685Total Points24,106Total Sites23,382Cell Towers69,500Racks Monitored56,753HVAC Units Monitored31,322UPS Units Monitored24,788Generators Monitored20,919,685Total Points24,106Total Sites23,382Cell Towers69,500Racks Monitored56,753HVAC Units Monitored31,322UPS Units Monitored24,788Generators Monitored

Chapter 3: Data Sources & ProtocolsLesson 19 of 65

Using Python with the Mango REST API

How to interact with Mango by Radix IoT programmatically using Python, including session authentication, JWT tokens, reading/writing point values, and querying historical data.

  • 14 min read
  • Intermediate
  • Mango 4.x / 5.x

Overview

Mango by Radix IoT exposes a comprehensive REST API that allows external applications to read and write data, manage configuration, and query historical values. Python, with its requests library, is an excellent choice for building integrations, automated reports, data migration scripts, and monitoring tools. This guide covers both session-based and JWT token authentication, common API operations, and complete example scripts.

Prerequisites

  • Mango 4.x or 5.x with the REST API enabled (enabled by default)
  • Python 3.7 or later installed on your workstation or server
  • The requests library installed (pip install requests)
  • A Mango user account with appropriate permissions for the operations you need
  • Network access from your Python environment to the Mango server

Step 1: Install Required Libraries

Open a terminal and install the dependencies:

pip install requests

For scripts that process time-series data, you may also want:

pip install pandas

Step 2: Session-Based Authentication

Session authentication works like a browser login. You post credentials to the login endpoint and receive a session cookie that you include in subsequent requests. Mango also uses XSRF (cross-site request forgery) tokens for security.

import requests
import uuid

# Configuration
MANGO_URL = "http://your-mango-server:8080"
USERNAME = "admin"
PASSWORD = "admin"

# Create a session to persist cookies
session = requests.Session()

# Generate an XSRF token (any cryptographically random string)
xsrf_token = str(uuid.uuid4())

# Set the XSRF token as both a cookie and a header
session.cookies.set("XSRF-TOKEN", xsrf_token)
session.headers.update({
    "X-XSRF-TOKEN": xsrf_token,
    "Content-Type": "application/json",
    "Accept": "application/json"
})

# Log in
login_response = session.post(
    f"{MANGO_URL}/rest/latest/login",
    json={"username": USERNAME, "password": PASSWORD}
)

if login_response.status_code == 200:
    print("Login successful")
else:
    print(f"Login failed: {login_response.status_code}")
    print(login_response.text)
    exit(1)

Note

The XSRF token can be any unique string generated using a cryptographically strong random method. The important thing is that the value in the X-XSRF-TOKEN header matches the value in the XSRF-TOKEN cookie. Mango validates that these match to prevent cross-site request forgery.

Step 3: JWT Token Authentication

JWT (JSON Web Token) authentication is simpler for scripts and automation because it does not require a login step or XSRF tokens. You generate a token once in the Mango UI and include it in every request as a Bearer token.

Generate a Token

  1. In Mango, navigate to Access management > Users
  2. Select the user account for API access
  3. Set an Expiry preset – the list runs 1 day, 1 week, 2 weeks, 1 month, 3 months, 6 months, 1 year, 2 years, 5 years, 10 years, and it defaults to 1 day. For anything off that list, set an Expiry date instead
  4. Click Create auth token
  5. The token appears in the Authentication token field with a Copy to clipboard button. Copy it immediately – there is no endpoint that lists or returns existing tokens, so it is unrecoverable

You can also mint one over REST with POST /rest/latest/auth-tokens/create, body {"username": "...", "expiry": "..."}, which answers 201 Created. Omit expiry on that path and you get a five-minute token – short enough to look like a bug rather than a setting.

Use the Token in Python

import requests

MANGO_URL = "http://your-mango-server:8080"
JWT_TOKEN = "your-jwt-token-here"

# Create a session with the JWT token
session = requests.Session()
session.headers.update({
    "Authorization": f"Bearer {JWT_TOKEN}",
    "Accept": "application/json",
    "Content-Type": "application/json"
})

# Test the connection
response = session.get(f"{MANGO_URL}/rest/latest/users/current")
if response.status_code == 200:
    user = response.json()
    print(f"Authenticated as: {user['username']}")
else:
    print(f"Authentication failed: {response.status_code}")

Tip

JWT tokens are the recommended approach for automated scripts, cron jobs, and service-to-service integrations. They do not require managing session state or XSRF tokens. Two things to know before you rely on them: anyone who authenticates with a password – basic auth or a session login – can create a token for themselves (authentication.mango-jwt.allowTokenCreation, default false, only decides whether a caller authenticated by a Mango JWT may mint another one), and revocation is user-wide – Mango tracks a token version per user, so revoking invalidates every token that user holds, not one of them.

Step 4: Read Point Values

Get a Single Point’s Current Value

def get_point_value(session, xid):
    """Get the current value of a data point by its XID."""
    # Note the segment order: /latest/{xid}, not /{xid}/latest
    response = session.get(
        f"{MANGO_URL}/rest/latest/point-values/latest/{xid}",
        params={"limit": 1}
    )
    if response.status_code == 200:
        # The endpoint returns a LIST, newest first -- not a single object
        data = response.json()
        if not data:
            print(f"Point {xid}: no values recorded")
            return None
        latest = data[0]
        print(f"Point {xid}: value={latest.get('value')}, "
              f"timestamp={latest.get('timestamp')}")
        return latest
    else:
        print(f"Error reading {xid}: {response.status_code}")
        return None

# Usage
get_point_value(session, "DP_Zone1_Temp")

Get Multiple Point Values at Once

def get_multiple_values(session, xids):
    """Get the latest values for multiple points."""
    # xids is a PATH variable, comma separated -- not a query parameter
    joined = ",".join(xids)
    response = session.get(
        f"{MANGO_URL}/rest/latest/point-values/multiple-arrays/latest/{joined}",
        params={"limit": 1}
    )
    if response.status_code == 200:
        return response.json()
    else:
        print(f"Error: {response.status_code}")
        return None

# Usage
values = get_multiple_values(session, [
    "DP_Zone1_Temp", "DP_Zone2_Temp", "DP_Zone3_Temp"
])

Step 5: Write / Set Point Values

To set a data point’s value, the user must have set permission on the target point, and the point must be settable.

def set_point_value(session, xid, value, data_type="NUMERIC"):
    """Set the value of a data point."""
    payload = {
        "dataType": data_type,
        "value": value
    }
    response = session.put(
        f"{MANGO_URL}/rest/latest/point-values/{xid}",
        json=payload
    )
    if response.status_code == 201:
        print(f"Successfully set {xid} to {value}")
    else:
        print(f"Error setting {xid}: {response.status_code}")
        print(response.text)

# Usage
set_point_value(session, "DP_Setpoint1", 72.5)

For different data types, adjust the dataType field:

Data Type dataType Value Example Value
Numeric NUMERIC 72.5
Binary BINARY true or false
Multistate MULTISTATE 2
Alphanumeric ALPHANUMERIC "Running"

Step 6: Query Historical Data

Retrieve time-series data for analysis, reporting, or export.

Get Values Over a Time Range

from datetime import datetime, timedelta

def get_historical_values(session, xid, from_time, to_time):
    """Get historical point values for a time range."""
    params = {
        "from": from_time.isoformat() + "Z",
        "to": to_time.isoformat() + "Z"
    }
    # History lives under /time-period/. A plain /point-values/{xid} has no
    # GET at all -- it is PUT and DELETE only, so a GET there returns 405.
    response = session.get(
        f"{MANGO_URL}/rest/latest/point-values/time-period/{xid}",
        params=params
    )
    if response.status_code == 200:
        data = response.json()
        print(f"Retrieved {len(data)} values for {xid}")
        return data
    else:
        print(f"Error: {response.status_code}")
        return None

# Get the last 24 hours of data
now = datetime.utcnow()
yesterday = now - timedelta(days=1)
history = get_historical_values(session, "DP_Zone1_Temp", yesterday, now)

Get Rollup/Aggregated Data

def get_rollup_values(session, xid, from_time, to_time,
                      rollup="AVERAGE", time_period_type="HOURS",
                      time_periods=1):
    """Get aggregated values with a specific rollup and period."""
    # The rollup is a PATH SEGMENT, not a query parameter. On this variant
    # from and to are REQUIRED, unlike the plain time-period endpoint.
    params = {
        "from": from_time.isoformat() + "Z",
        "to": to_time.isoformat() + "Z",
        "timePeriodType": time_period_type,
        "timePeriods": time_periods
    }
    response = session.get(
        f"{MANGO_URL}/rest/latest/point-values/time-period/{xid}/{rollup}",
        params=params
    )
    if response.status_code == 200:
        return response.json()
    else:
        print(f"Error: {response.status_code}")
        return None

# Get hourly averages for the past week
last_week = now - timedelta(days=7)
rollups = get_rollup_values(session, "DP_Zone1_Temp",
                            last_week, now,
                            rollup="AVERAGE",
                            time_period_type="HOURS",
                            time_periods=1)

Step 7: Error Handling and Best Practices

Robust Error Handling

from requests.exceptions import ConnectionError, Timeout

def safe_api_call(session, method, url, **kwargs):
    """Make an API call with proper error handling."""
    try:
        response = session.request(method, url, timeout=30, **kwargs)
        response.raise_for_status()
        return response.json()
    except ConnectionError:
        print(f"Cannot connect to Mango at {MANGO_URL}")
        return None
    except Timeout:
        print(f"Request timed out: {url}")
        return None
    except requests.exceptions.HTTPError as e:
        print(f"HTTP error {e.response.status_code}: {e.response.text}")
        return None

SSL/TLS Certificate Handling

If Mango uses a self-signed SSL certificate:

# Option 1: Disable verification (development only)
session.verify = False

# Option 2: Point to your CA bundle (recommended for production)
session.verify = "/path/to/ca-bundle.crt"

Session Cleanup

Always close sessions when done:

# Log out when using session authentication
session.post(f"{MANGO_URL}/rest/latest/logout")
session.close()

Complete Example: Daily Report Script

import requests
import csv
from datetime import datetime, timedelta

MANGO_URL = "http://your-mango-server:8080"
JWT_TOKEN = "your-jwt-token-here"
POINT_XIDS = ["DP_Zone1_Temp", "DP_Zone2_Temp", "DP_AHU1_kW"]

def main():
    session = requests.Session()
    session.headers.update({
        "Authorization": f"Bearer {JWT_TOKEN}",
        "Accept": "application/json"
    })

    now = datetime.utcnow()
    yesterday = now - timedelta(days=1)

    # Collect daily averages for each point
    report_data = []
    for xid in POINT_XIDS:
        params = {
            "from": yesterday.isoformat() + "Z",
            "to": now.isoformat() + "Z",
            "timePeriodType": "HOURS",
            "timePeriods": "1"
        }
        resp = session.get(
            f"{MANGO_URL}/rest/latest/point-values/time-period/{xid}/AVERAGE",
            params=params
        )
        if resp.status_code == 200:
            for entry in resp.json():
                report_data.append({
                    "xid": xid,
                    "timestamp": entry.get("timestamp"),
                    "value": entry.get("value")
                })

    # Write to CSV
    filename = f"daily_report_{yesterday.strftime('%Y%m%d')}.csv"
    with open(filename, "w", newline="") as f:
        writer = csv.DictWriter(f,
            fieldnames=["xid", "timestamp", "value"])
        writer.writeheader()
        writer.writerows(report_data)

    print(f"Report saved to {filename}")
    session.close()

if __name__ == "__main__":
    main()

Verification

Confirm your Python integration is working:

  • Session or JWT authentication succeeds (200 response from login or current-user endpoint)
  • You can read current point values for known XIDs
  • You can write values to settable points (verify the change in the Mango UI)
  • Historical queries return data for valid time ranges
  • Error handling catches connection failures and invalid responses gracefully

Troubleshooting

401 Unauthorized on every request

  • For session auth: check that the login call returned 200. If it did not, every later request runs unauthenticated
  • For JWT auth: ensure the token has not expired and the header is formatted as Bearer <token> (note the space after Bearer)
  • Check that the user account is not disabled in Mango

403 Forbidden on the login or on a write

  • For session auth, the login and every later POST, PUT or DELETE need an X-XSRF-TOKEN header that matches the XSRF-TOKEN cookie. A mismatch is a 403, not a 401

403 Forbidden when reading or writing a point

  • The user does not have the required read or set permissions on the data point
  • Check the point’s permissions on the Data point tab of the point editor: View point & value to read it, Set point value to write it (Edit point governs changing its configuration)

Connection refused or timeout

  • Verify the Mango server is running and accessible from the Python host
  • Check the port number and protocol (HTTP vs. HTTPS)
  • If using HTTPS with a self-signed certificate, configure session.verify appropriately

Empty response when querying historical data

  • The time range may not contain any logged values — verify the point is logging and the time range is correct
  • Use ISO 8601 format with a Z suffix for UTC times
  • Check that the point XID is correct (XIDs are case-sensitive)

Get free access to all 65 Mango by Radix IoT training lessons

One short form unlocks every lesson and its video in this browser.

This form is hosted by Zoho. It loads only after you enable support cookies or choose to load it below.

We honored your browser’s Global Privacy Control signal — optional cookies are off and nothing is tracked.