Mobexa REST API
Automate mobile application security testing end to end. Upload Android and iOS builds, poll asynchronous scan jobs, and pull findings, SARIF and quality-gate results straight into your CI/CD pipeline. Every endpoint returns JSON over HTTPS and is authenticated with a single API key header.
Introduction
The Mobexa API is a resource-oriented REST API. It accepts and returns JSON, uses standard HTTP verbs and status codes, and is served only over HTTPS. The base URL for every request is https://mobexa.io/api.
Each request is scoped to the tenant that owns the API key, so you only ever see your own applications, scans and reports. There is no public, unauthenticated data access beyond the health probe.
Authentication
Authenticate every request by sending your key in the X-API-Key request header. Keys are created in the dashboard under Settings, then API Keys. A key is shown once at creation time and is stored on the server only as a SHA-256 hash, so it cannot be recovered later; if you lose it, rotate it.
curl https://mobexa.io/api/apps \
-H "X-API-Key: mbx_live_xxxxxxxxxxxxxxxxxxxxxxxx"Treat an API key like a password. Inject it from an environment variable or CI secret, never embed it in a mobile app, browser bundle or public repository, and rotate it immediately if it is exposed.
Quickstart
From zero to a gated pipeline in eight steps.
- Create an API keyIn the dashboard open Settings, then API Keys, and generate a key. Keys are shown once and stored only as a SHA-256 hash on the server.
- Keep the key secretStore it as an environment variable or CI secret. Never commit it to source control and never expose it in client-side code.
- Check connectivityCall the public health endpoint to confirm the service is reachable from your network.
- List your applicationsSend your first authenticated request with the X-API-Key header.
- Upload a build from CIPush an APK, AAB or IPA to start a scan as part of your pipeline.
- Poll the jobA scan runs asynchronously. Poll the job until its status is completed.
- Pull the report or SARIFFetch the full JSON findings, or SARIF for native code-scanning integrations.
- Gate the pipelineRead the quality-gate verdict and fail the build when it does not pass.
Your first request
List the applications in your tenant. Pick your language; the request is identical, only the client changes.
curl https://mobexa.io/api/apps \
-H "X-API-Key: mbx_live_xxxxxxxxxxxxxxxxxxxxxxxx"
import os
import requests
resp = requests.get(
"https://mobexa.io/api/apps",
headers={"X-API-Key": os.environ["MOBEXA_API_KEY"]},
timeout=30,
)
resp.raise_for_status()
print(resp.json())
const res = await fetch("https://mobexa.io/api/apps", {
headers: { "X-API-Key": process.env.MOBEXA_API_KEY },
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
console.log(await res.json());
$ch = curl_init("https://mobexa.io/api/apps");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("MOBEXA_API_KEY")],
]);
$body = curl_exec($ch);
curl_close($ch);
echo $body;
package main
import (
"io"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://mobexa.io/api/apps", nil)
req.Header.Set("X-API-Key", os.Getenv("MOBEXA_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
io.Copy(os.Stdout, resp.Body)
}
require "net/http"
require "uri"
uri = URI("https://mobexa.io/api/apps")
req = Net::HTTP::Get.new(uri)
req["X-API-Key"] = ENV.fetch("MOBEXA_API_KEY")
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.body
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://mobexa.io/api/apps"))
.header("X-API-Key", System.getenv("MOBEXA_API_KEY"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
using var client = new HttpClient();
client.DefaultRequestHeaders.Add(
"X-API-Key",
Environment.GetEnvironmentVariable("MOBEXA_API_KEY"));
var response = await client.GetAsync("https://mobexa.io/api/apps");
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let key = std::env::var("MOBEXA_API_KEY").unwrap();
let body = reqwest::Client::new()
.get("https://mobexa.io/api/apps")
.header("X-API-Key", key)
.send()
.await?
.text()
.await?;
println!("{}", body);
Ok(())
}
import okhttp3.OkHttpClient
import okhttp3.Request
val client = OkHttpClient()
val request = Request.Builder()
.url("https://mobexa.io/api/apps")
.header("X-API-Key", System.getenv("MOBEXA_API_KEY"))
.build()
client.newCall(request).execute().use { response ->
println(response.body?.string())
}
Example response
{
"apps": [
{
"id": "5f3a9c2b8e1d4a7c",
"package_name": "com.example.app",
"platform": "android",
"latest_score": 82,
"monitoring": true
}
]
}Field values above are illustrative. Identifiers, scores and platform values reflect your own applications at request time.
Conventions
Requests that send a body use Content-Type: application/json, except binary uploads, which use multipart/form-data. Resource identifiers are opaque strings; do not parse them. Timestamps are ISO 8601 in UTC. Unknown fields in a response should be ignored so your integration keeps working as the API grows.
Errors
The API uses conventional HTTP status codes. A failed request returns a JSON body with a single error string describing the problem.
{ "error": "API key required" }| Status | Meaning | When |
|---|---|---|
200 | OK | Request succeeded. |
201 | Created | A resource (application, job) was created. |
400 | Bad Request | Malformed request or missing required field. |
401 | Unauthorized | The X-API-Key header is missing or empty. |
403 | Forbidden | The key is invalid, or not permitted for this resource. |
404 | Not Found | The resource id does not exist in your tenant. |
429 | Too Many Requests | Rate limit exceeded. Back off and retry after a short delay. |
5xx | Server Error | A transient server-side error. Retry with exponential backoff. |
Rate limits
Automated clients should be resilient. When a request returns 429, back off and retry after a short delay, and use exponential backoff for 5xx responses. Scans run asynchronously, so poll job status on an interval (for example every 15 seconds) rather than in a tight loop.
Endpoints
The core resources are applications, jobs, scans and reports. A lock means the endpoint requires the X-API-Key header.
Upload a build
Send a binary as multipart form data to start a scan from CI.
curl -X POST https://mobexa.io/api/apps/upload \
-H "X-API-Key: mbx_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-F "[email protected]"CI/CD gate
A common pipeline pattern: upload the build, wait for the scan to finish, then read the quality-gate verdict and fail the job if it does not pass. Pull /reports/{id}/sarif if your platform ingests SARIF for code scanning.
JOB=$(curl -s -X POST https://mobexa.io/api/apps/upload \
-H "X-API-Key: $MOBEXA_API_KEY" \
-F "[email protected]" | jq -r '.job_id')
while true; do
STATUS=$(curl -s https://mobexa.io/api/jobs/$JOB \
-H "X-API-Key: $MOBEXA_API_KEY" | jq -r '.status')
[ "$STATUS" = "completed" ] && break
[ "$STATUS" = "failed" ] && { echo "scan failed"; exit 1; }
sleep 15
doneCLI and tooling
A command-line client ships for pipelines that prefer a single binary over raw HTTP. It reads the same base URL and key from the MOBEXA_API_KEY environment variable and wraps upload, polling and gating into one command.
Build your integration
Create an API key in the dashboard, or talk to our team about enterprise automation, on-premise deployment and SARIF pipelines.
Talk to our team See the platform