Reverse Engineering the Dahua DVR API
How I probed 106 undocumented RPC2 services on a Dahua XVR to build a working Python client from scratch.

I wanted to see who was accessing my Dahua DH-XVR4104HS-X security cameras and when. The DVR has a web interface with a log viewer built in, but I wanted to pull that data programmatically and build something on top of it. One problem: Dahua publishes almost no API documentation for this device.
So I had to figure it out.
First attempt — the CGI API
Dahua DVRs expose a CGI interface at /cgi-bin/log.cgi. I tried the obvious:
GET /cgi-bin/log.cgi?action=getLog&StartTime=2026-06-01&EndTime=2026-06-27
400 Bad Request. Tried a dozen parameter variations. All 400s. The CGI log endpoint simply isn't supported on this firmware version.
But other CGI endpoints worked fine:
GET /cgi-bin/configManager.cgi?action=getConfig&name=Network→ table.Network.eth0.IPAddress=192.168.1.3 table.Network.eth0.PhysicalAddress=38:af:29:9f:9a:ee ...
So the device was reachable and auth was working (Dahua uses HTTP Digest auth, not Basic). The log endpoint was just missing.
The RPC2 API
By sniffing the browser traffic when using the DVR's own web interface, I found requests going to /RPC2 and /RPC2_Login. This is Dahua's JSON-RPC API — undocumented publicly, but used internally by their web UI.
JSON-RPC means every call hits the same endpoint. The function name goes in the request body:
POST /RPC2{ "method": "userManager.getActiveUserInfoAll", "params": {}, "id": 1, "session": "abc123"}
The catch: authentication isn't standard. Dahua uses a two-step MD5 challenge:
- Send a login request with an empty password — the DVR responds with a
randomstring and arealm - Compute:
MD5(username + ":" + realm + ":" + password)→ call thispwd_md5 - Compute:
MD5(username + ":" + random + ":" + pwd_md5)→ this is yourauthorization - Send the second login with
authorizationas the password
Once past auth, I needed to find what methods actually existed.
Discovering 106 services
POST /RPC2{ "method": "system.listService", "params": {}, "id": 1, "session": "..." }
That returned 106 service names: userManager, log, mediaFileFind, snapManager, ptz, eventManager, netApp, recordManager, and more.
For each service I called .listMethod to get its full method list, then tested each one to see what actually worked. A lot returned "Method not found!" or "Request invalid param!" — you just have to probe.
The surprises
The log API accepted requests but I got zero results. Turns out the parameters have to be wrapped in a condition object with capitalized keys — not documented anywhere:
{ "condition": { "StartTime": "2026-06-01 00:00:00", "EndTime": "2026-06-27 23:59:59" } }
Login events aren't typed "Login" — they're "Account.LogIn" and "Account.LogOut". Found this by fetching all event types and counting them.
The most useful endpoint turned out to be one I found by reading through the userManager method list: userManager.getActiveUserInfoAll. It returns every currently connected client with their real IP address, login time, and session ID. This is far more useful than the log history for real-time monitoring.
What the API actually covers
After probing everything, here's what works reliably:
- Device info — model, serial number, firmware version, CPU
- Network — interface status, MAC address, public IP via UPnP, P2P cloud tunnel status
- Active sessions — real-time connected clients with IPs and login times
- User management — list accounts, permissions, disconnect sessions
- Log history — login/logout events with timestamps and client types
- Storage — HDD partitions, capacity, usage, error state
- Config — any configuration section via the CGI interface
The Python client
I wrapped everything into a simple importable Python module:
from dahua_client import session_context, get_active_sessions, get_storage_info with session_context("192.168.1.3", "yourpassword") as s: for session in get_active_sessions(s): print(session["username"], session["ip"], session["login_time"]) for disk in get_storage_info(s): print(disk["partition"], f"{disk['used_pct']}% full")
The full client is on GitHub at github.com/DeniKucevic/dahua-xvr-api with all confirmed-working endpoints documented.
What I learned
When there are no docs, the approach is: find what endpoint the official UI uses, authenticate the same way it does, then enumerate systematically. system.listService → .listMethod on each service → test each method with empty params → read the error codes to understand what's wrong.
The DVR's logging also has a real limitation worth knowing: when you access cameras through Dahua's DMSS mobile app, the connection routes through their P2P cloud relay. From the DVR's perspective, that connection always comes from 127.0.0.1 — you can't see the real device IP. Direct LAN connections do show real IPs. Something to keep in mind if you're building access monitoring on top of this.