Skip to content

Conversation

@maroffo
Copy link

@maroffo maroffo commented Dec 17, 2025

Summary

Add automatic retry with exponential backoff for Figma API rate limiting.

Context: As of November 17, 2025, Figma has updated their API rate limits. See the official announcement and rate limits documentation.

This PR handles:

  • HTTP 429 (Too Many Requests) responses with automatic retry using the Retry-After header
  • Request timeouts with exponential backoff (1s → 2s → 4s)
  • Graceful exit when Retry-After exceeds 5 minutes (avoids indefinite waiting)

Changes

  • Add RateLimitError enum for rate limiting error types
  • Add RetryConfiguration struct for customizable retry behavior
  • Add HTTPURLResponse extension to extract Retry-After header
  • Add requestWithRetry() method to Client protocol and BaseClient
  • Update FigmaClient to support retry configuration
  • Update all loaders to use requestWithRetry()
  • Add FigmaAPITests test target with rate limiting tests

Default Configuration

Setting Value
Max retries 3
Max Retry-After wait 300s (5 min)
Initial timeout backoff 1s
Backoff multiplier 2.0x

Test Plan

  • All existing tests pass (67 tests)
  • New rate limiting tests pass (12 tests)
  • Builds on macOS

  - Handle HTTP 429 responses with Retry-After header
  - Implement exponential backoff for request timeouts
  - Exit gracefully when Retry-After exceeds 5 minutes
  - Add RetryConfiguration for customizable retry behavior
  - Update all loaders to use requestWithRetry()
  - Add FigmaAPITests for rate limiting functionality
}

logger.warning("Rate limited by Figma API. Waiting \(Int(retryAfter))s before retry \(attempt + 1)/\(configuration.maxRetries)")
Thread.sleep(forTimeInterval: retryAfter)
Copy link
Collaborator

@subdan subdan Dec 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bad practice to use Thread.sleep. It blocks the thread. Try to use Timer or DispatchQueue+asyncAfter

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Replaced Thread.sleep with a hybrid RunLoop-based implementation that keeps the thread responsive during the wait. When RunLoop has active sources, it processes pending callbacks; otherwise, it falls back to short Thread.sleep(0.1) intervals to ensure the delay actually elapses (pure RunLoop would return immediately on threads without input sources).

Additionally, I've added a configurable requestDelay parameter (under figma: in YAML) to proactively throttle requests between API calls, which helps prevent rate limiting when exporting large numbers of assets.

Example config:

figma:
  lightFileId: "..."
  requestDelay: 0.1  # 100ms between requests

  Add requestDelaySeconds to RetryConfiguration to throttle API requests and prevent hitting Figma rate limits when
  exporting many assets.

  - Add requestDelay config option under figma: section in YAML
  - Apply delay after each successful API request when configured
  - Log throttling info when delay is active
  - Default to 0 (disabled) for backward compatibility
  - Preserve full RetryConfiguration flexibility in FigmaClient
- Import FoundationNetworking on Linux for HTTPURLResponse
- Use Thread.sleep directly on Linux where RunLoop behaves differently
- Add test for requestDelaySeconds configuration
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants