Format Your New Topic as Follows:
API Endpoint(s) and/or Zoom API Event(s)
PUT /webinars/{webinarId}/registrants/status
Link the API endpoint(s) and/orZoom API Event(s) you’re working with to help give context.
→ /api/rest/reference/zoom-api/methods/#operation/webinarRegistrantStatus
Description
We organized a webinar for 1000 attendees.
We registered and approved attendees one by one by APIs call in a row.
- POST /webinars/{webinarId}/registrants
- PUT /webinars/{webinarId}/registrants/status
Only 10 attendees were able to join the webinar. We got 429 status code on all our queries after.
Error?
The full error message or issue you are running into, where applicable.
“You have exceeded the daily rate limit of (10) for webinar Update registrant's status API requests for the registrant”
It seems rate limit aren’t applied as describe. We made Unit Test to test it :
- We create 30 attendees one by one running :
- POST /webinars/{webinarId}/registrants
then - PUT /webinars/{webinarId}/registrants/status
- Always failed to the eleventh
Registrants (id and email) are always different, we are using the registrantId given from the POST response to the PUT query.
Means the limit describe isn’t working as expected : “Registrant status requests
10 requests per day (UTC) for the same registrant in the same meeting/webinar”
How To Reproduce
Steps to reproduce the behavior:
Here are our 2 tests files. (well… I had to copy, I can’t join file to the topic)
There is a fixture file containing 30 users with different emails, then this PHP File :
class ZoomApiTest extends KernelTestCase
{
use IntegrationTestTrait;
protected function setUp(): void
{
$kernel = self::bootKernel(['env' => 'test']);
$this->app = new Application($kernel);
}
/**
* Testing the Zoom rate-limit for registrant status request
* https://developers.zoom.us/docs/api/rate-limits/
*/
public function testAddingAttendeesToWebinar(): void
{
// Load the fixtures
$this->loadFixture(__DIR__ . '/../../../features/bootstrap/fixtures/webinar/webinar_playwright_load_testing.yml');
/** @var WebinarAttendeeRepository $attendeesRepository */
$attendeesRepository = static::getContainer()->get(WebinarAttendeeRepository::class);
/** @var WebinarRepository $webinarRepository */
$webinarRepository = static::getContainer()->get(WebinarRepository::class);
/** @var WebinarAttendee[] | null $attendees */
$attendees = $attendeesRepository->findBy(['status' => WebinarAttendee::STATUS_ATTENDEE]);
$webinar = $webinarRepository->find('webinar2');
$this->assertInstanceOf(Webinar::class, $webinar);
// 30 attendees
$this->assertCount(30, $attendees);
try {
$webinarResponse = $this->createWebinar($webinar);
$this->assertInstanceOf(CreateWebinarResponse::class, $webinarResponse);
$this->assertNotEmpty($webinarResponse->getZoomWebinarId());
} catch (ZoomException $e) {
$this->fail($e->getMessage());
}
foreach ($attendees as $key => $attendee) {
try {
$response = $this->joinWebinarAsAttendee($attendee, $webinarResponse->getZoomWebinarId());
$this->assertInstanceOf(JoinWebinarResponse::class, $response);
} catch (ZoomException $e) {
// Rate limit after 10 users
// 10 requests per day (UTC) for the same registrant in the same meeting/webinar
$this->assertStringContainsString('You have exceeded the daily rate limit of (10) for webinar Update registrant\'s status API requests for the registrant', $e->getMessage());
$this->assertGreaterThanOrEqual(10, $key);
}
}
}
/**
* @throws ZoomException
* @throws DateInvalidTimeZoneException
*/
public function createWebinar(Webinar $webinar): CreateWebinarResponse
{
$dateDiff = $webinar->getStartDate()->diff($webinar->getEndDate());
$duration = $dateDiff->i + ($dateDiff->h * 60);
$startDate = clone $webinar->getStartDate();
$startDate->setTimezone(new DateTimeZone($webinar->getTimezone()));
$response = $this->callApi(
'POST',
'/v2/users/me/webinars',
[
'agenda' => $webinar->getSummary(),
"topic" => $webinar->getTitle(),
"start_time" => $startDate->format('Y-m-d\TH:i:s'),
"duration" => $duration,
"timezone" => $webinar->getTimezone(),
"type" => 5,
// Manually approve
"settings" => [
"approval_type" => 1,
"allow_multiple_devices" => false,
"auto_recording" => "cloud",
"panelists_invitation_email_notification" => false,
"registrants_email_notification" => false,
"practice_session" => true,
"panelists_video" => true,
"host_video" => true,
"question_and_answer" => [
"enabled" => true,
"allow_submit_questions" => true,
"allow_anonymous_questions" => false,
"answer_questions" => "all",
"attendees_can_comment" => true,
"attendees_can_upvote" => true,
]
]
]
);
try {
$data = $response->toArray();
return new CreateWebinarResponse($data['id'], $data['start_url']);
} catch (ExceptionInterface $e) {
throw new ZoomException('An error occurred while creating webinar', 0, $e);
}
}
public function joinWebinarAsAttendee(WebinarAttendee $attendee, string $webinarZoomId): JoinWebinarResponse
{
$response = $this->callApi(
'POST',
'/v2/webinars/' . $webinarZoomId . '/registrants',
[
'email' => $attendee->getUser()->getEmail(),
'first_name' => $attendee->getUser()->getFirstName(),
'last_name' => $attendee->getUser()->getLastName(),
'address' => $attendee->getUser()->getAddress(),
'language' => 'fr-FR'
]
);
$addRegistrantResponseData = $response->toArray();
if ($response->getStatusCode() !== 201) {
$message = $response->toArray(false)['message'] ?? 'An error occurred while adding registrant';
throw new ZoomException($message);
}
$response = $this->callApi(
'PUT',
'/v2/webinars/' . $webinarZoomId . '/registrants/status',
[
'action' => 'approve',
'registrants' => [
[
'id' => $addRegistrantResponseData['id'],
'email' => $attendee->getUser()->getEmail(),
]
]
]
);
if ($response->getStatusCode() !== 204) {
$message = $response->toArray(false)['message'] ?? 'An error occurred while adding registrant';
throw new ZoomException($message);
}
return new JoinWebinarResponse($addRegistrantResponseData['join_url']);
}
private function callApi(string $httpVerb, string $uri, array $jsonPayload = null): ResponseInterface
{
$httpClient = HttpClient::createForBaseUri('https://zoom.us');
$options = [
'headers' => [
"Authorization" => "Bearer {$this->getAccessToken()}"
]
];
if ($jsonPayload) {
$options['json'] = $jsonPayload;
}
try {
return $httpClient->request($httpVerb, $uri, $options);
} catch (ClientException|TransportExceptionInterface $e) {
throw new ZoomException('An error occurred while calling Zoom API', 0, $e);
}
}
private function getAccessToken(): string
{
$httpClient = HttpClient::createForBaseUri('https://zoom.us');
$accountId = self::getContainer()->getParameter('zoomAccountId');
$clientId = self::getContainer()->getParameter('zoomClientId');
$clientSecret = self::getContainer()->getParameter('zoomClientSecret');
// get access token from zoom
$response = $httpClient->request('POST', '/oauth/token', [
'query' => [
'grant_type' => 'account_credentials',
'account_id' => $accountId,
],
'headers' => [
'Authorization' => 'Basic ' . base64_encode("{$clientId}:{$clientSecret}")
]
]);
return $response->toArray()['access_token'];
}
}
Could you please, take a look, and explain.
Regards,