---
title: GET /recordings
source: https://se-hari.com/docs/api/api-recordings-list
updated: 2026-08-15T18:52:41.772082+00:00
---

> Daftar rekaman. GET /recordings — butuh scope recordings:read.

Menelusuri rekaman milik pemilik API key. Field `note_id` menunjukkan notulen yang sudah dibuat dari rekaman tersebut, atau null kalau belum pernah diproses. Filter `has_note=false` adalah cara menemukan rekaman yang masih menganggur — ini kombinasi paling berguna di seluruh API, karena rekaman meeting Anda sudah otomatis diarsipkan dan sebagian besar belum pernah dijadikan notulen. Rekaman disimpan 30 hari sejak dibuat; setelah `expires_at` lewat, asetnya hilang dan tidak bisa lagi diunduh maupun dijadikan notulen. Kunci penyimpanan tidak diekspos — yang tersedia hanya penanda `has_audio_asset` dan `has_transcript_asset` yang menunjukkan kelengkapan aset.
## Endpoint
```bash
GET https://se-hari.com/api/v1/recordings
```
Butuh scope `recordings:read`. Scope ditetapkan saat key dibuat dan **tidak bisa ditambahkan belakangan** — kalau key Anda kurang izin, buat key baru lalu cabut yang lama. Kekakuan ini disengaja: scope yang bisa dinaikkan diam-diam berarti key yang bocor hari ini bisa menjadi lebih berbahaya besok. Beri setiap integrasi key sendiri dengan scope sesempit mungkin, supaya mencabut satu integrasi tidak pernah berarti mematikan yang lain.
Daftar ini memakai **paginasi cursor**, bukan offset. Ambil `next_cursor` dari respons lalu kirim kembali sebagai `starting_after` untuk halaman berikutnya, dan berhenti saat `has_more` bernilai `false`. Cursor dipilih karena offset melewatkan baris begitu ada data baru masuk di tengah penelusuran — pada daftar yang bertambah setiap hari itu bukan kasus langka melainkan kejadian harian, dan gejalanya adalah baris yang hilang tanpa satu pun error.
## Parameter query

| Parameter | Tipe | Wajib | Keterangan |
|---|---|---|---|
| `limit` | `integer` | ya | Jumlah baris per halaman (1-100). min 1, maks 100 |
| `starting_after` | `string` | tidak | — |
| `source` | `zoom` \| `upload` | tidak | — |
| `has_note` | `true` \| `false` | tidak | — |
| `meeting_id` | `string` | tidak | — |

## Contoh
```bash
curl -X GET "https://se-hari.com/api/v1/recordings?limit=10&starting_after=nilai" \
  -H "Authorization: Bearer $SEHARI_API_KEY"
```
```javascript
const res = await fetch('https://se-hari.com/api/v1/recordings', {
	method: 'GET',
	headers: {
			'Authorization': `Bearer ${process.env.SEHARI_API_KEY}`,
		}
});

if (!res.ok) {
	const { error } = await res.json();
	// error.code stabil dan bisa dicabang; error.message untuk manusia.
	throw new Error(`${error.code}: ${error.message}`);
}

const data = await res.json();
```
```python
import os, requests

res = requests.get(
    "https://se-hari.com/api/v1/recordings",
    headers={"Authorization": f"Bearer {os.environ['SEHARI_API_KEY']}"},
    timeout=30,
)

if not res.ok:
    err = res.json()["error"]
    raise RuntimeError(f"{err['code']}: {err['message']}")

data = res.json()
```
## Field tiap item di `data`

| Field | Tipe | Keterangan |
|---|---|---|
| `id` | `string` | — |
| `meeting_id` | `string` \| `null` | — |
| `source` | `string` | — |
| `status` | `string` | — |
| `size_mb` | `number` \| `null` | — |
| `duration_seconds` | `number` \| `null` | — |
| `original_file_name` | `string` \| `null` | — |
| `has_audio_asset` | `boolean` | — |
| `has_transcript_asset` | `boolean` | — |
| `note_id` | `string` \| `null` | — |
| `expires_at` | `string` \| `null` | — |
| `created_at` | `string` | — |

## Menelusuri semua halaman

Satu request hanya mengembalikan maksimum 100 baris. Untuk mengambil semuanya, ikuti `next_cursor` sampai `has_more` bernilai `false` — jangan menghitung sendiri posisinya.

```javascript
async function ambilSemua() {
	const semua = [];
	let cursor = null;

	do {
		const qs = new URLSearchParams({ limit: "100" });
		if (cursor) qs.set("starting_after", cursor);

		const res = await fetch(`https://se-hari.com/api/v1/recordings?${qs}`, {
			headers: { Authorization: `Bearer ${process.env.SEHARI_API_KEY}` }
		});
		const page = await res.json();

		semua.push(...page.data);
		cursor = page.has_more ? page.next_cursor : null;
	} while (cursor);

	return semua;
}
```

## Kalau gagal
Setiap kegagalan memakai amplop yang sama, dan `code` di dalamnya stabil — cabangkan logika Anda ke sana, jangan ke `message` yang teksnya bisa diperbaiki sewaktu-waktu.
```json
{
  "error": {
    "code": "invalid_parameter",
    "message": "Penjelasan untuk manusia.",
    "docs_url": "https://se-hari.com/docs/error/invalid-parameter",
    "request_id": "req_a1b2c3d4e5f6",
    "details": {}
  }
}
```
| Status | Kode | Artinya di endpoint ini |
|---|---|---|
| 400 | [`invalid_parameter`](/docs/error/invalid-parameter) | Ada parameter query yang salah bentuk atau di luar rentang. |
| 401 | [`invalid_api_key`](/docs/error/invalid-api-key) | Key salah, sudah dicabut, atau kedaluwarsa. |
| 403 | [`insufficient_scope`](/docs/error/insufficient-scope) | Key valid, tapi tidak punya scope yang dibutuhkan endpoint ini. |
| 429 | [`rate_limited`](/docs/error/rate-limited) | Terlalu banyak request. Hormati header `Retry-After`. |
| 500 | [`internal_error`](/docs/error/internal-error) | Kesalahan di sisi kami. Sertakan `request_id` saat melapor. |
Simpan `request_id` dari setiap respons gagal. Itu satu-satunya cara kami menemukan kembali request Anda di log ketika Anda melapor, dan ia juga ada di header `X-Request-Id` pada respons yang berhasil.
## Alur khas

Rekaman disimpan **30 hari** lalu dihapus otomatis. Angka itu menentukan hampir semua keputusan di sekitar endpoint rekaman: tautan unduh dibuat sesaat, arsip harus dijadwalkan, dan rekaman lama menjawab `gone` alih-alih menyajikan berkas yang sudah tidak ada.

1. `GET /recordings` — cari rekaman yang Anda butuhkan
2. `POST /recordings/{id}/download-url` — buat tautan bertanda tangan
3. Unduh berkasnya SEGERA; tautan berlaku singkat dan tidak bisa disimpan untuk nanti
4. Salin ke penyimpanan Anda sendiri kalau perlu disimpan lebih dari 30 hari

Tautan unduh sengaja berumur pendek karena ia memberi akses tanpa API key kepada siapa pun yang memegangnya. Menyimpannya di database atau mengirimnya lewat email sama dengan membagikan isi rekaman kepada siapa saja yang kelak membaca catatan itu.

## Yang sering keliru

- **Key disalin sebagian.** API key Se-Hari panjangnya tetap: `sh_live_` diikuti 43 karakter. Kalau panjangnya berbeda, yang salah adalah proses penyalinannya, bukan key-nya — dan pesan 401 tidak akan mengatakan itu.
- **Scope kurang, bukan key salah.** 403 di sini berarti key-nya dikenali. Periksa scope-nya lewat `GET /me`, lalu buat key baru kalau memang kurang; scope tidak bisa ditambahkan ke key yang sudah jadi.
- **Hormati `Retry-After`.** Mencoba lagi lebih cepat dari yang disebutkan hanya memperpanjang masa tunggu. Kalau Anda sering menyentuhnya, kemungkinan besar Anda sedang polling sesuatu yang seharusnya ditangani webhook.

## Selanjutnya
- [Autentikasi dan API key](/docs/autentikasi) — cara membuat key dan memilih scope
- [Kode error](/docs/error) — arti setiap kode dan cara memperbaikinya
- [Idempotency](/docs/idempotency) — kenapa retry aman kalau dilakukan dengan benar
- [Spesifikasi OpenAPI](https://se-hari.com/api/v1/openapi.json) — kontrak mesin, cocok untuk men-generate klien