Use getCloudRecords to enumerate Imou cloud records newest-first. Send an authorized token, deviceId, channelId, time window, and count of at most 30; optionally set cloudType to video or snapshot (video is the default). For the next page, set endTime to the minimum beginTime returned. The documented response codes are type 1000, 2000, and 10001, while encryptMode is 0 or 1.
Why it matters
Cloud-record timelines often fail at the edges: the first page looks correct, but older clips disappear, records repeat, or snapshots and videos are mixed unintentionally. The reliable fix is to implement the exact cursor strategy documented by the API rather than inventing an offset.
getCloudRecords sorts matching records in descending order by begin and end time and returns the previous count records. It has no documented page number. The time boundary is the pagination state.
Request and response model
| Field | Direction | Documented meaning |
|---|---|---|
token |
Request | Administrator or sub-account accessToken
|
deviceId |
Request | Device serial number |
channelId |
Request | Channel ID |
beginTime |
Request | Start of query window, yyyy-MM-dd HH:mm:ss
|
endTime |
Request | End of query window; moves backward for later pages |
count |
Request | Number requested, maximum 30 |
cloudType |
Request | Optional video or snapshot; default video
|
records[].beginTime |
Response | Record start and the key used to move the boundary |
records[].endTime |
Response | Record end |
records[].recordId |
Response | Recording ID |
records[].encryptMode |
Response |
0 default encryption; 1 user encryption |
records[].type |
Response | Documented recording type code |
For a sub-account token, the page states the minimum permission is RecordReplay on cam:serial-number:channel-number.
Reverse-pagination architecture
UI requests newest records in a window
|
v
Backend checks tenant + channel authorization
|
v
getCloudRecords(beginTime, endTime, count<=30, cloudType)
|
v
Return normalized records + opaque application cursor
|
v
Cursor stores minimum returned beginTime
|
+---- next page: use it as the new endTime
The browser does not need administrator credentials or request-signing secrets. Your backend can expose an opaque cursor that represents the documented time boundary while retaining the original device, channel, media type, and lower bound.
Six steps to enumerate safely
1. Fix the query scope
Choose one authorized deviceId and channelId, plus an inclusive business time window. Keep the lower beginTime fixed throughout one traversal. Select cloudType: "video" or "snapshot" explicitly when clarity matters.
2. Request the first page
The relevant parameter block is:
{
"params": {
"token": "AUTHORIZED_ACCESS_TOKEN",
"deviceId": "DEVICE123",
"channelId": "0",
"beginTime": "2026-08-31 09:00:00",
"endTime": "2026-08-31 11:00:00",
"count": 30,
"cloudType": "video"
}
}
The full OpenAPI request also requires the documented signed system envelope.
3. Read and normalize the records
Preserve at least recordId, device/channel, begin/end times, size, thumbnail URL, encryptMode, recordRegionId, and type when present. Do not convert an unknown future type into a guessed label.
4. Derive the next boundary
Find the minimum beginTime among records in the response. For the next call, keep the original lower beginTime and set endTime to that minimum returned start time.
5. Apply the documented stop conditions
The API page says traversal can be considered complete when:
- the minimum returned
beginTimeis less than or equal to the query’s lower begin time; or - fewer records are returned than the requested
count.
An empty response also leaves no next boundary, so the application ends the traversal.
6. Defend the UI against boundary overlap
Because the previous minimum beginTime becomes the next endTime, an implementation should de-duplicate by a stable returned identifier such as recordId when merging pages. This is defensive application guidance; the official page defines the boundary movement and stop rules, not a duplicate-free client cache guarantee.
Recording type codes
The current getCloudRecords response table explicitly documents:
type |
Meaning |
|---|---|
1000 |
Event cloud recording |
2000 |
Continuous cloud recording |
10001 |
Humanoid detection cloud recording |
Only label these values from this page. Do not infer adjacent values, create a numbering pattern, or claim that the list covers every event category that may ever exist. A robust application can display an unknown value as “Other” or retain the raw code until the live documentation defines it.
Understanding encryptMode
The response field is documented as:
encryptMode |
Meaning |
|---|---|
0 |
Default encryption mode |
1 |
User encryption mode |
It is a returned property, not a getCloudRecords filter parameter. Do not claim the list API decrypts content or that every playback path handles both modes identically. Pass the field into the approved playback/decryption workflow supported by the relevant SDK or interface documentation.
Video versus snapshot enumeration
cloudType controls which cloud-record media category to query:
-
video: cloud video recording; also the documented default. -
snapshot: snapshot recording.
This is separate from records[].type, which labels documented recording types in the returned record. Do not use type: 1000 as a substitute for cloudType: "snapshot" or assume all event recordings are snapshots.
Pseudo-code
lower = requestedBeginTime
upper = requestedEndTime
seen = set()
while true:
records = getCloudRecords(
deviceId, channelId,
beginTime=lower,
endTime=upper,
count=30,
cloudType="video"
)
emit each record whose recordId is not in seen
add emitted recordIds to seen
if records.length < 30:
break
nextUpper = minimum(records.beginTime)
if nextUpper <= lower:
break
upper = nextUpper
The API supports count values up to 30, so the sample uses 30. The in-memory seen set is illustrative; production systems can de-duplicate at the page merge or persistence layer.
Limits and pitfalls
Sending count above 30
The documented maximum is 30 per getCloudRecords request. Split the traversal into pages.
Inventing pageNo or offset
This reverse-order interface uses the time boundary, not a documented numeric page cursor.
Moving the wrong edge
Keep the original lower beginTime; move endTime backward to the minimum returned beginTime.
Guessing type codes
Publish only 1000, 2000, and 10001 with the meanings stated above. Treat any other value as undefined by this page until an official source says otherwise.
Treating encryptMode as a query option
It is a response field with documented values 0 and 1. The request parameter table does not define an encryptMode filter.
Assuming one page is a complete incident
A full page of 30 explicitly signals that more records may exist. Follow the boundary and stop conditions.
Losing query context in cursors
Bind your application cursor to the user, tenant, device, channel, lower bound, and cloudType. Otherwise a cursor can be replayed against the wrong scope. This is backend security guidance.
When forward order is preferable
Imou also documents queryCloudRecords, which sorts in ascending order and uses a different queryRange model. Use that interface only when forward traversal is the actual product requirement. Do not mix its limit or pagination rules into getCloudRecords; this article’s 30-record cap and minimum-beginTime cursor apply to the reverse-order API.
Register at Imou Open Platform to test reverse cloud-record pagination against your authorized device channels and actual storage services.
Top comments (0)