{"record":{"id":"c6004224fb860fa0","repo":"we-promise/sure","slug":"trading-212-rate-limit-exceeded-please-wait-befor","errorCode":null,"errorMessage":"Trading 212 rate limit exceeded. Please wait before retrying.","messagePattern":"Trading 212 rate limit exceeded\\. Please wait before retrying\\.","errorType":"exception","errorClass":"Provider::Trading212::RateLimitError","httpStatus":429,"severity":"warning","filePath":"app/models/provider/trading212.rb","lineNumber":137,"sourceCode":"      items\n    end\n\n    def extract_cursor(next_page_path)\n      uri = URI.parse(\"https://placeholder#{next_page_path}\")\n      params = URI.decode_www_form(uri.query.to_s).to_h\n      params[\"cursor\"]\n    rescue URI::InvalidURIError\n      nil\n    end\n\n    def handle_response(response)\n      case response.code\n      when 200, 201\n        response.parsed_response\n      when 401, 403\n        raise AuthenticationError, \"Trading 212 authentication failed (#{response.code}). Check your API key.\"\n      when 429\n        raise RateLimitError, \"Trading 212 rate limit exceeded. Please wait before retrying.\"\n      else\n        raise ApiError.new(\n          \"Trading 212 API error (status #{response.code})\",\n          status_code: response.code,\n          response_body: response.body\n        )\n      end\n    end\n\n    def with_retries(label, max_retries: 3)\n      attempt = 0\n      begin\n        attempt += 1\n        yield\n      rescue *RETRYABLE_ERRORS => e\n        raise if attempt >= max_retries\n        delay = [ 2**attempt, 30 ].min\n        DebugLogEntry.capture(","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/trading212.rb#L119-L155","documentation":"Provider::Trading212::RateLimitError raised in handle_response when the Trading 212 API answers HTTP 429. Trading 212 enforces roughly 1 request/second per connection and tighter limits on history endpoints (the client even sleeps 10s between history pages), so bursts trigger this response.","triggerScenarios":"Calling fetch_account_summary/fetch_positions in quick succession; parallel jobs syncing the same or multiple Trading 212 accounts; ignoring the pacing in fetch_all_pages (which sleeps 10s per page because history endpoints allow ~6 req/min); an immediate retry after a previous request.","commonSituations":"Background jobs overlapping for different users on the same API key; manual console testing while a sync job runs; retry logic without backoff hammering the API.","solutions":["Wait before retrying: back off at least 1–10 seconds (history endpoints need ~10s between calls) and use exponential backoff","Serialize requests per API key (a mutex or single worker) so concurrent jobs don't burst past ~1 req/s","Rescue Provider::Trading212::RateLimitError separately from AuthenticationError and schedule a delayed retry (e.g. retry_job wait: 30) instead of failing the sync","Reduce page volume by keeping the built-in PAGE_LIMIT/MAX_PAGES pagination rather than fetching unpaginated"],"exampleFix":"// before\nbegin\n  client.fetch_account_summary\nrescue Provider::Trading212::Error\n  retry # tight loop -> 429 loop\nend\n\n// after\nbegin\n  client.fetch_account_summary\nrescue Provider::Trading212::RateLimitError\n  sleep 30\n  retry\nrescue Provider::Trading212::AuthenticationError\n  raise # do not retry auth failures\nend","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"begin\n  client.fetch_positions\nrescue Provider::Trading212::RateLimitError\n  sleep 30 * attempt # exponential-ish backoff, cap attempts\n  retry if (attempt += 1) <= 3\nrescue Provider::Trading212::AuthenticationError\n  raise\nend","preventionTips":["Serialize Trading 212 requests per API key (single worker/mutex) to stay under ~1 req/s","Keep the built-in 10s sleep between history pages — history endpoints allow only ~6 req/min","Rescue RateLimitError distinctly from AuthenticationError so backoff applies only where it helps"],"tags":["ruby","trading212","rate-limit","http-429","backoff"],"backgroundTag":"rate-limit-exceeded","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}