1. What the REST API Is and Why It Matters
Veeam Backup and Replication v13 exposes its entire operational surface through a REST API running on port 9419 over HTTPS. This is not the Enterprise Manager REST API. This is the VBR server's own API, available on every VBR installation without Enterprise Manager deployed. The distinction matters because the two APIs have different endpoints, different authentication models, and different capability sets. If you are reading Veeam documentation and see references to the EM REST API, that is a separate thing entirely.
The VBR REST API uses OAuth 2.0 bearer token authentication, returns JSON, and conforms to OpenAPI Specification 3.0. The full spec is available as a swagger.json file in the VBR installation directory and through the built-in Swagger UI at https://<vbr-server>:9419/api/swagger/ui/index.html. The current API revision for v13.0.1 is 1.3-rev1.
Why use it instead of the PowerShell module? Three reasons. First, the REST API is language-agnostic. You can call it from Python, Go, Bash with curl, a monitoring platform webhook, or an automation tool like n8n or Ansible. Second, it does not require the Veeam PowerShell module to be installed on the machine making the call. Third, for MSPs running VSPC v9, REST API proxying lets you route API calls through VSPC to all managed VBR servers using a single API key, which is significantly cleaner than managing PowerShell remoting sessions across dozens of backup servers.
TWO APIS, TWO PORTS
The VBR REST API runs on port 9419. The Enterprise Manager REST API runs on port 9398 (by default). They are not interchangeable. This article covers the VBR REST API exclusively.
2. Prerequisites and Connectivity
The REST API service starts automatically with VBR. No additional installation or configuration is required. During the VBR install, a self-signed TLS certificate is generated and bound to port 9419. If you are calling the API from a script, you will need to either trust that certificate or skip certificate validation in your HTTP client.
Requirements
1. VBR v13.0.1 or later installed and running. The REST API was introduced in v11 but v13.0.1 massively expanded the endpoint coverage.
2. Network access to port 9419/TCP on the VBR server from the machine running your scripts.
3. A Veeam user account with appropriate RBAC role. Backup Administrator for full access, Backup Operator for read-only monitoring.
4. An HTTP client. PowerShell with Invoke-RestMethod, curl, Python requests, or any tool that speaks HTTPS.
Verify connectivity before writing any code:
curl -k https://<vbr-server>:9419/api/v1/serverTime
If this returns a JSON response with the server version and time, the API service is running and reachable. If it times out, check your firewall rules for port 9419.
3. Authentication: OAuth 2.0 Token Flow
Every API call requires a bearer token in the Authorization header. You get that token by posting credentials to the OAuth 2.0 token endpoint. The token has a default lifetime of 15 minutes. The refresh token lasts 24 hours by default regardless of the use_short_term_refresh parameter (this is a known behavior in v13.0.1 that does not match the documentation's stated formula of access token lifetime plus 15 minutes).
Getting an Access Token (curl)
TOKEN=$(curl -sk -X POST "https://<vbr-server>:9419/api/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "x-api-version: 1.3-rev1" \
-d "grant_type=password&username=DOMAIN%5Cusername&password=YourPassword" \
| jq -r '.access_token')
echo $TOKEN
The response includes access_token, refresh_token, and expires_in (in seconds). Save the refresh token. When the access token expires, use the refresh token to get a new pair without re-authenticating with credentials.
Getting an Access Token (PowerShell)
$vbrServer = "vbr01.yourdomain.local"
$cred = Get-Credential -Message "VBR credentials"
$body = @{
grant_type = "password"
username = $cred.UserName
password = $cred.GetNetworkCredential().Password
}
$auth = Invoke-RestMethod -Uri "https://${vbrServer}:9419/api/oauth2/token" `
-Method Post -Body $body -SkipCertificateCheck `
-Headers @{ "x-api-version" = "1.3-rev1" }
$token = $auth.access_token
$refresh = $auth.refresh_token
$headers = @{
"Authorization" = "Bearer $token"
"x-api-version" = "1.3-rev1"
"Accept" = "application/json"
}
Refreshing an Expired Token
$refreshBody = @{
grant_type = "refresh_token"
refresh_token = $refresh
}
$newAuth = Invoke-RestMethod -Uri "https://${vbrServer}:9419/api/oauth2/token" `
-Method Post -Body $refreshBody -SkipCertificateCheck `
-Headers @{ "x-api-version" = "1.3-rev1" }
$token = $newAuth.access_token
$refresh = $newAuth.refresh_token
$headers["Authorization"] = "Bearer $token"
SECURITY NOTE
Never hardcode credentials in scripts. Use Get-Credential, environment variables, or a secrets manager. For scheduled scripts, store the refresh token in a secure vault and use it to obtain new access tokens without storing the password.
Authorization Code Delegation
The v13 API also supports an authorization code grant type for delegating access between clients. Client A (which has a valid token) requests a temporary authorization code from /api/oauth2/authorization_code. Client B exchanges that code for its own access token. The code expires in 60 seconds. This is useful for scenarios where a central orchestrator needs to hand off API access to a worker process without sharing the original credentials.
4. Core Endpoints: Jobs, Sessions, and Repositories
The API organizes resources into logical sections. Here are the endpoints you will use most often for monitoring and orchestration.
| Endpoint | Method | What It Returns |
| /api/v1/jobs | GET | All configured backup, replication, and copy jobs with schedule, type, and enabled state |
| /api/v1/jobs/{id} | GET | Single job detail including virtual machines in the job scope |
| /api/v1/sessions | GET | Job sessions with status, start/end time, result, and bottleneck data |
| /api/v1/backups | GET | All backup chains with restore point count, size, and repository location |
| /api/v1/repositories | GET | Backup repositories with type, capacity, free space, and path |
| /api/v1/managedServers | GET | Managed infrastructure servers (vCenter, Hyper-V hosts, Linux servers) |
| /api/v1/credentials | GET | Credential records stored on the backup server |
| /api/v1/configBackup | GET | Configuration backup settings and last run status |
| /api/v1/encryptionPasswords | GET | Encryption password records (not the passwords themselves) |
| /api/v1/malwareDetection | GET | Malware detection events from SureBackup scan sessions |
All collection endpoints support pagination with skip and limit query parameters, filtering with nameFilter (supports wildcards), and type filtering with typeFilter. For example, to get only failed job sessions:
GET /api/v1/sessions?typeFilter=Job&resultFilter=Failed&limit=50
5. Triggering and Managing Backup Jobs
Starting a backup job through the API is a POST to the job's start endpoint. The job runs asynchronously. The API returns a session ID that you can poll to track progress.
Start a Job
# Start a backup job by ID
$jobId = "c36824e9-0c3e-4892-8dff-44fd56968506"
Invoke-RestMethod -Uri "https://${vbrServer}:9419/api/v1/jobs/${jobId}/start" `
-Method Post -Headers $headers -SkipCertificateCheck
Stop a Running Job
Invoke-RestMethod -Uri "https://${vbrServer}:9419/api/v1/jobs/${jobId}/stop" `
-Method Post -Headers $headers -SkipCertificateCheck
Poll Session Status
# Get the most recent session for a specific job
$sessions = Invoke-RestMethod `
-Uri "https://${vbrServer}:9419/api/v1/sessions?jobIdFilter=${jobId}&limit=1" `
-Method Get -Headers $headers -SkipCertificateCheck
$latest = $sessions.data[0]
Write-Host "State: $($latest.state) | Result: $($latest.result)"
Session states include Working, Idle, and Stopped. Results include Success, Warning, Failed, and None (while still running).
6. Building a Health Check Script
This script authenticates, pulls all job sessions from the last 24 hours, and flags anything that did not succeed. This is the foundation for any monitoring integration.
param(
[Parameter(Mandatory)]
[string]$VBRServer,
[Parameter(Mandatory)]
[PSCredential]$Credential
)
# Authenticate
$authBody = @{
grant_type = "password"
username = $Credential.UserName
password = $Credential.GetNetworkCredential().Password
}
$auth = Invoke-RestMethod -Uri "https://${VBRServer}:9419/api/oauth2/token" `
-Method Post -Body $authBody -SkipCertificateCheck `
-Headers @{ "x-api-version" = "1.3-rev1" }
$headers = @{
"Authorization" = "Bearer $($auth.access_token)"
"x-api-version" = "1.3-rev1"
}
# Pull sessions from the last 24 hours
$since = (Get-Date).AddHours(-24).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
$allSessions = @()
$skip = 0
$pageSize = 100
do {
$batch = Invoke-RestMethod `
-Uri "https://${VBRServer}:9419/api/v1/sessions?typeFilter=Job&limit=${pageSize}&skip=${skip}" `
-Method Get -Headers $headers -SkipCertificateCheck
$allSessions += $batch.data
$skip += $pageSize
} while ($batch.data.Count -eq $pageSize)
# Filter to last 24 hours
$recent = $allSessions | Where-Object {
$_.creationTime -ge $since
}
# Report
$failed = $recent | Where-Object { $_.result -eq "Failed" }
$warning = $recent | Where-Object { $_.result -eq "Warning" }
$success = $recent | Where-Object { $_.result -eq "Success" }
Write-Host "=== VBR Health Check: $VBRServer ==="
Write-Host "Period: Last 24 hours"
Write-Host "Total sessions: $($recent.Count)"
Write-Host "Success: $($success.Count) | Warning: $($warning.Count) | Failed: $($failed.Count)"
if ($failed.Count -gt 0) {
Write-Host "`nFailed Jobs:" -ForegroundColor Red
$failed | ForEach-Object {
Write-Host " - $($_.name) | Started: $($_.creationTime)" -ForegroundColor Red
}
}
if ($warning.Count -gt 0) {
Write-Host "`nWarning Jobs:" -ForegroundColor Yellow
$warning | ForEach-Object {
Write-Host " - $($_.name) | Started: $($_.creationTime)" -ForegroundColor Yellow
}
}
To integrate this with a monitoring platform, replace the Write-Host output with a webhook call, SNMP trap, or syslog message. The data is the same. The delivery mechanism is up to you.
7. Repository Capacity Monitoring
Repository capacity is one of the most common things you need to track programmatically. The /api/v1/repositories endpoint returns total and free space for each repository.
$repos = Invoke-RestMethod `
-Uri "https://${VBRServer}:9419/api/v1/repositories" `
-Method Get -Headers $headers -SkipCertificateCheck
foreach ($repo in $repos.data) {
$totalGB = [math]::Round($repo.capacityGB, 1)
$freeGB = [math]::Round($repo.freeGB, 1)
$usedPct = if ($totalGB -gt 0) {
[math]::Round((($totalGB - $freeGB) / $totalGB) * 100, 1)
} else { 0 }
$color = if ($usedPct -gt 90) { "Red" }
elseif ($usedPct -gt 75) { "Yellow" }
else { "Green" }
Write-Host "$($repo.name): ${usedPct}% used (${freeGB} GB free of ${totalGB} GB)" `
-ForegroundColor $color
}
For SOBR extents, the parent SOBR object in the API response includes references to its performance and capacity tier extents. Query each extent individually for granular capacity data.
8. Encryption Password Verification
This is one of the most operationally valuable endpoints added in v13.0.1. It lets you programmatically verify that an encryption password stored in VBR is still correct. If you have ever been in the position of needing to restore encrypted backups and not being certain the stored password is valid, this endpoint solves that problem before it becomes a crisis.
# List all encryption passwords
$passwords = Invoke-RestMethod `
-Uri "https://${VBRServer}:9419/api/v1/encryptionPasswords" `
-Method Get -Headers $headers -SkipCertificateCheck
foreach ($pw in $passwords.data) {
# Verify each password
$verifyBody = @{ password = "your-known-password" } | ConvertTo-Json
try {
$result = Invoke-RestMethod `
-Uri "https://${VBRServer}:9419/api/v1/encryptionPasswords/$($pw.id)/verify" `
-Method Post -Headers ($headers + @{ "Content-Type" = "application/json" }) `
-Body $verifyBody -SkipCertificateCheck
Write-Host "PASS: $($pw.description)" -ForegroundColor Green
}
catch {
Write-Host "FAIL: $($pw.description)" -ForegroundColor Red
}
}
COMPLIANCE USE CASE
Schedule this verification weekly. Export the results to a log. Auditors want proof that your encryption keys are valid and that you can actually decrypt your backups. This script provides that evidence automatically.
9. Malware Detection Scan Logs
When SureBackup runs a malware scan against restore points, the results are available through the /api/v1/malwareDetection endpoints. This is the API surface for building automated clean room verification reporting.
# Get malware detection events
$events = Invoke-RestMethod `
-Uri "https://${VBRServer}:9419/api/v1/malwareDetection/events" `
-Method Get -Headers $headers -SkipCertificateCheck
foreach ($event in $events.data) {
$status = if ($event.detectionType -eq "Clean") { "CLEAN" } else { "DETECTED" }
Write-Host "$status | $($event.machineName) | $($event.detectionTime)"
}
# Get detailed scan session log for a specific task
$scanLog = Invoke-RestMethod `
-Uri "https://${VBRServer}:9419/api/v1/malwareDetection/scanSessionLog/$taskSessionId" `
-Method Get -Headers $headers -SkipCertificateCheck
Pair this with the encryption password verification from the previous section and the health check script to build a comprehensive daily compliance report that covers job success, backup integrity, encryption key validity, and malware scan results. All from the same API surface.
10. REST API vs PowerShell: When to Use Which
The VBR PowerShell module and the REST API overlap in capability but serve different use cases. Neither replaces the other entirely.
| Scenario | Best Tool | Why |
| Remote monitoring from a non-Windows system | REST API | No PowerShell module or Windows dependency required |
| Multi-server monitoring through VSPC | REST API | VSPC v9 REST API proxying routes calls through a single key |
| Integration with Grafana, PRTG, or n8n | REST API | HTTP calls are natively supported in every monitoring platform |
| Complex job creation with all options | PowerShell | PowerShell cmdlets expose every job parameter. REST API job creation is more limited. |
| Bulk operations on local VBR server | PowerShell | Faster iteration. No token management overhead. |
| Tape job management | PowerShell | Tape endpoints are not yet fully covered in the REST API as of v13.0.1 |
| Agent job management | PowerShell | Agent job types were limited in REST API through v12.x. Coverage is expanding in v13 but PowerShell is still more complete. |
The general rule: use the REST API when you need remote, cross-platform, or multi-server access. Use PowerShell when you need full feature coverage on a single VBR server and you are already running on Windows.
11. Operational Gotchas
The x-api-version header is mandatory. Every request to the VBR REST API must include x-api-version: 1.3-rev1 (for v13.0.1). Omit it and you get a 400 Bad Request. This catches everyone the first time.
Self-signed certificate handling. The default VBR install uses a self-signed TLS cert on port 9419. In PowerShell, use -SkipCertificateCheck. In curl, use -k. In production, replace the self-signed cert with one from your internal CA and bind it to port 9419 using netsh http add sslcert. The Veeam help center documents the exact binding command.
Token refresh behavior in v13.0.1. The documentation states that the refresh token lifetime is the access token lifetime plus 15 minutes. In practice, the refresh token consistently expires at 24 hours regardless of the use_short_term_refresh flag. This is a known community-reported behavior. Build your token refresh logic around a 24-hour window, not the documented formula.
Pagination is not optional for large environments. Collection endpoints default to returning a limited set of results. Always implement pagination with skip and limit parameters in a loop until the returned count is less than your page size. If you have 500 jobs and you pull the default page, you will miss data.
REST API coverage is still expanding. Not every VBR feature has a REST API endpoint yet. Veeam has stated they are adding coverage with every release. As of v13.0.1, the API covers vSphere backup jobs, Hyper-V backup jobs, replication jobs, VCD jobs, managed servers (VMware, Windows, Linux), repositories, sessions, credentials, encryption, malware detection, configuration backup, and the mass deployment automation endpoints. Plug-in workloads (Nutanix AHV, Proxmox, oVirt, HyperCore) and NAS backup jobs have limited or no REST API coverage yet.
Username encoding. When passing domain credentials in the token request body, backslash characters must be URL-encoded as %5C. The value DOMAIN\username becomes DOMAIN%5Cusername in the POST body.
Rate limiting. There is no documented rate limit on the VBR REST API, but hammering a VBR server with thousands of requests per minute will impact the Veeam.Backup.Service process. For monitoring scripts, a polling interval of 60 seconds or longer is sensible. For one-shot health checks, run them once and exit.
Key Takeaways
✓ The VBR REST API runs on port 9419, separate from the Enterprise Manager API on 9398
✓ Authentication uses OAuth 2.0 with bearer tokens. Access tokens expire in 15 minutes. Refresh tokens last 24 hours in practice.
✓ The x-api-version header (1.3-rev1 for v13.0.1) is required on every request
✓ Core endpoints cover jobs, sessions, repositories, credentials, encryption passwords, and malware detection events
✓ Encryption password verification and malware scan logs are the two most operationally valuable endpoints added in v13.0.1
✓ The Swagger UI at port 9419 is a live, interactive reference that lets you test calls directly against your VBR server
✓ Use the REST API for remote and cross-platform monitoring. Use PowerShell when you need full feature coverage on a local Windows VBR server.
✓ Always implement pagination for collection endpoints in environments with more than a handful of jobs
Published on anystackarchitect.com | Author: Eric Black | Veeam v13 Series
