What a Video Chat API Taught Me About Good API Design
A hands-on guide to consuming a real video chat API in Python, and what makes its design worth learning from.
Most developers learn APIs by reading docs. The best developers also learn how to read docs — and what separates good API design from frustrating ones.
In this post, we'll do both. We'll integrate the VideoConnect API (a REST API for real-time video chat) using Python, and along the way break down what makes its design clean and worth copying.
What is the VideoConnect API?
VideoConnect lets you programmatically create and manage video chat rooms, authenticate participants, and retrieve session data — all over HTTPS with JSON.
Base URL: https://api.videoconnect.io/v1
Think of it like the backend engine behind a Zoom or Google Meet, but one you control entirely via API calls.
Step 1 — Authentication
Before you can do anything, you need a JWT token. VideoConnect uses a client_id + client_secret flow — common in service-to-service APIs.
import requests
BASE_URL = "https://api.videoconnect.io/v1"
def get_token(client_id: str, client_secret: str) -> str:
response = requests.post(
f"{BASE_URL}/auth/token",
json={
"client_id": client_id,
"client_secret": client_secret
}
)
response.raise_for_status()
return response.json()["access_token"]
token = get_token("your_client_id", "your_client_secret")
headers = {
"Authorization": f"Bearer {token}"
}
The token expires in 3600 seconds (1 hour). Short-lived tokens are a security best practice.
Always cache the token and refresh it before expiry rather than fetching a new one on every request.
Step 2 — Create a Room
A Room is the core resource. Every video session lives inside one.
def create_room(name: str, max_participants: int = 10) -> dict:
response = requests.post(
f"{BASE_URL}/rooms",
headers=headers,
json={
"name": name,
"max_participants": max_participants,
"is_private": False,
"recording": False
}
)
response.raise_for_status()
return response.json()
room = create_room("Weekly Standup")
print(room["join_url"])
# output: https://meet.videoconnect.io/rm_8xKp2mNqLz
The response gives you a join_url you can share directly with participants — no extra steps needed.
Step 3 — Retrieve Room Details
Once a room is active, you can poll its status, participant count, duration, and whether it's still live.
def get_room(room_id: str) -> dict:
response = requests.get(
f"{BASE_URL}/rooms/{room_id}",
headers=headers
)
response.raise_for_status()
return response.json()
details = get_room("rm_8xKp2mNqLz")
print(
f"{details['participant_count']} participants | "
f"{details['duration_seconds']}s"
)
Room Status Values
| Status | Meaning |
|---|---|
waiting |
Room created, no one has joined yet |
active |
At least one participant is connected |
closed |
Room has ended |
Step 4 — Delete a Room
When a session ends, clean up:
def delete_room(room_id: str) -> None:
response = requests.delete(
f"{BASE_URL}/rooms/{room_id}",
headers=headers
)
response.raise_for_status()
print(response.json()["message"])
delete_room("rm_8xKp2mNqLz")
# output:
# Room rm_8xKp2mNqLz has been closed.
Step 5 — Handle Errors Properly
VideoConnect returns consistent, machine-readable error objects on every failure.
{
"error": "room_not_found",
"message": "No room with id rm_xyz exists.",
"status": 404
}
Here is a clean way to handle them in Python:
class VideoConnectError(Exception):
def __init__(self, error_code: str, message: str, status: int):
self.error_code = error_code
self.status = status
super().__init__(message)
def safe_get_room(room_id: str) -> dict:
response = requests.get(
f"{BASE_URL}/rooms/{room_id}",
headers=headers
)
if not response.ok:
err = response.json()
raise VideoConnectError(
err["error"],
err["message"],
err["status"]
)
return response.json()
try:
room = safe_get_room("rm_invalid")
except VideoConnectError as e:
print(f"[{e.status}] {e.error_code}: {e}")
# output:
# [404] room_not_found: No room with id rm_invalid exists.
The error field is a machine-readable code separate from the human-readable message.
This lets you handle errors in code without string-matching on messages that could change.
Putting It All Together
Here is a complete script that creates a room, checks its status, and cleans up:
import requests
import time
BASE_URL = "https://api.videoconnect.io/v1"
def main():
token = get_token(
"your_client_id",
"your_client_secret"
)
headers = {
"Authorization": f"Bearer {token}"
}
room = create_room(
"Team Sync",
max_participants=5
)
room_id = room["room_id"]
print(f"Room created: {room['join_url']}")
time.sleep(5)
details = get_room(room_id)
print(
f"Status: {details['status']} | "
f"Participants: {details['participant_count']}"
)
delete_room(room_id)
print("Room closed.")
if __name__ == "__main__":
main()
What Makes This API Well-Designed
After working through the integration, a few things stand out.
Every error returns the same
{error, message, status}structureResource IDs are prefixed with
rm_Fields like
is_privateandrecordinghave sensible defaults/v1/versioning prevents breaking integrations laterResponses include useful computed fields like
join_url
These small design choices massively improve developer experience.
Key Takeaways
Cache JWT tokens and refresh before expiry
Use machine-readable error codes
Prefix resource IDs
Return useful computed response fields
Keep API responses predictable and consistent
Follow GajDev for more posts on APIs, backend engineering, DevOps, and developer tooling.

