Rate Limiting Kepler services

This topic describes how to set up Rate Limiting for Kepler services. These instructions apply to Relativity Server 2024 and later, and are intended for Relativity administrators. You will need access to SQL Server Management Studio and the EDDS database, or the Instance Settings console, to set up the rate limiting described here.

What is rate limiting?

Rate limiting puts a speed limit on how often a particular Relativity service can be called in a given amount of time.

Think of it like a tollbooth with a sign that says "no more than 1,000 cars per minute". Cars under that limit pass through normally. Once the limit is reached, additional cars are turned away until the next minute starts. Kepler rate limiting works the same way for calls made to Relativity's web services (called Kepler services) — it protects the system from being overwhelmed by too many requests in too short a time.

Rate limiting is only enforced for a service once an administrator configures a limit for it with an instance setting. Until you do that, no limit is applied.

How it works

Once you've configured a limit for a service, every call to that service passes through a checkpoint before it reaches the service itself:

  • Under the limit — the request goes through normally, and the response includes a few informational usage headers
  • Limit reached for this time window — the request is blocked with an HTTP 429 response, and the caller must wait and retry.

See the section What callers see when a limit is hit for more information on the headers and behavior.

This works automatically for normal Relativity traffic — there is no separate step you need to take to make calls count toward the limit. The only thing that determines whether a limit applies is whether you have configured one for that service in the instance setting.

Before you begin

Prerequisites:

  • Access to SQL Server Management Studio (or another tool for running SQL) connected to the EDDS database, or access to the Relativity Instance Settings console (Server > Instance Settings) if you'd rather add the setting through the User Interface instead of SQL.
  • A maintenance window. Changing this setting does not require restarting Relativity, but it's good practice to make configuration changes at a quiet time.
  • The name of the Kepler service you want to limit, if you are only limiting one or two services rather than all of them.
  • Measure your traffic before picking a number. There is no one-size-fits-all limit. The right number of requests per time window depends on how many users and workspaces you have, and how much of your traffic is bulk exports, large productions, or automated integrations. A limit that's safe for a small environment could throttle real users in a larger one. Before setting a limit, look at your own recent traffic for the services you're protecting — for example, your Kepler metrics or IIS logs — to find a realistic peak, and set your limit with a safety margin above it, rather than guessing.
You do not need to change any application code. Everything in this guide is configuration only with a single instance setting.

Create the instance setting

This is the setting that turns rate limiting on for a service and defines its limit. Add it either through the Relativity user interface, or with SQL.

Option A: Add it through the Relativity UI

Go to Home > Instance Settings > New Instance Setting and fill in:

Field Value
Name RateLimitConfiguration
Section Relativity.Platform.Discovery
Machine Leave blank (applies to all servers)
Value Type Text
Value Your rate limit rule, for example r=Relativity.Services.Environmental.IEnvironmentModule;l=3;w=10

Option B: Add it with SQL instead

Copy
EXEC [eddsdbo].CreateInstanceSetting
    @section = 'Relativity.Platform.Discovery',
    @name = 'RateLimitConfiguration',
    @machineName = '',
    @value = 'r=Relativity.Services.Environmental.IEnvironmentModule;l=1000;w=60',
    @description = 'A list of rate limit configurations, one for each Kepler Module which is being rate limited.',
    @valueType = 'Text',
    @initialValue = 'r=Relativity.Services.Environmental.IEnvironmentModule;l=1000;w=60',
    @createAsSystemArtifact = 0
Changes take a moment to apply. Relativity refreshes instance settings on a regular cycle (about every 30 seconds), so a new or updated limit takes effect shortly after you save it. No server restart is needed.

Reading and writing the setting's value

The value field looks technical, but it follows one simple pattern: three letters, each followed by an equal sign and a number or name, separated by semicolons.

Letter Stands for What you put there
r Route The name of the Kepler service being limited, for example Relativity.Services.Environmental.IEnvironmentModule
l Limit The maximum number of calls allowed in the time window, for example 1000
w Window The length of the time window, in seconds, for example 60 for one minute

So r=Relativity.ObjectManager;l=1000;w=60 reads as: allow up to 1,000 calls to the ObjectManager service every 60 seconds. Letters can be upper- or lower-case.

To set limits on more than one service, separate each rule with a pipe character (|):

r=Relativity.Services.Environmental.IEnvironmentModule;l=1000;w=60|r=Relativity.ObjectManager;l=1000;w=30
To turn rate limiting off again, edit the setting and clear the value so it's an empty string. Don't delete the setting itself. Deleting it can leave the last known limit active until the service restarts, which isn't what most people expect.

Configuring every service at once

If you want a limit applied to every Kepler service rather than typing a rule for each one by hand, every registered Kepler service has an entry in a table called ApplicationServiceIdentity. The following script is an example of how you could enumerate all of those services with SQL and automatically build a value in the r/l/w format. It is a starting point for your own automation, not a script to copy as-is.

Before running anything like this, measure your traffic first and choose @NumberOfRequests and @TimespanInSeconds based on what you actually observe. Applying one arbitrary limit to every service at once, without measuring first, risks throttling legitimate traffic on your busier endpoints.

Copy
DECLARE @NumberOfRequests INT = 1000;
-- the L (limit) parameter — set from your own measured traffic
DECLARE @TimespanInSeconds INT = 60;
-- the W (window) parameter — set from your own measured traffic
IF (
  NOT EXISTS(
    SELECT 
      1 
    FROM 
      [EDDS].[eddsdbo].[InstanceSetting] 
    WHERE 
      [Section] = N 'Relativity.Platform.Discovery' 
      AND [Name] = N 'RateLimitConfiguration' 
      AND [MachineName] = ''
  )
) BEGIN DECLARE @configurationValue NVARCHAR(MAX);
SET 
  @configurationValue = (
    SELECT 
      STRING_AGG(
        CAST(
          CONCAT(
            'r=', [Identifier], ';L=', @NumberOfRequests, 
            ';W=', @TimespanInSeconds
          ) AS NVARCHAR(MAX)
        ), 
        '|'
      ) WITHIN GROUP (
        ORDER BY 
          [Identifier]
      ) AS RateLimitConfiguration 
    FROM 
      EDDS.eddsdbo.ApplicationServiceIdentity
  );
IF (@configurationValue IS NULL) BEGIN 
SET 
  @configurationValue = '';
END 
EXEC [eddsdbo].CreateInstanceSetting @section = 'Relativity.Platform.Discovery', 
@name = 'RateLimitConfiguration', 
@machineName = '', 
@value = @configurationValue, 
@description = 'A list of rate limit configurations, one for each Kepler Module which is being rate limited.', 
@valueType = 'Text', 
@initialValue = @configurationValue, 
@createAsSystemArtifact = 0;
END
As written, this script applies the same number of requests and time window to every service it finds, which is why measuring traffic first matters — a single number won't be right for every endpoint. If a particular service needs its own, different limit, edit that one rule afterward.

The script would need to be modified if the instance setting already exists. The [eddsdbo].CreateInstanceSetting stored procedure won't overwrite an existing setting.

What callers see when a limit is hit

Once a limit is active, every response to a rate-limited call carries a few extra, informational headers.

Under the limit (HTTP 200) — the call goes through normally, and the response includes:

  • RateLimit-Limit — the limit for this service
  • RateLimit-Remaining — calls left in this time window
  • RateLimit-Reset — seconds until the window resets

Limit reached (HTTP 429) — the call is blocked until the window resets. The caller receives:

  • An HTTP 429 status code
  • A Retry-After header — seconds to wait before trying again
  • A message body: {"message": "API rate limit exceeded"}

Applications built on .NET that call a rate-limited service directly will see this as a TooManyRequestsException rather than a raw 429. Its Delta value tells the application how long to wait, matching the Retry-After header.

Frequently asked questions

Do I need to restart Relativity for a change to take effect?
No. Instance setting changes are picked up automatically on Relativity's normal refresh cycle (about every 30 seconds). New limits apply starting with the next time window for that service.

How do I find the exact name to use for the "r" (route) value?
Each Kepler service has an entry in the ApplicationServiceIdentity table, in a column named Identifier. That identifier is exactly the value to use for r=. The illustration script reads this column automatically for every service.

What happens if I make a typo in the value field?
A rule that doesn't match the expected r=…;l=…;w=… pattern is simply ignored for that entry, rather than causing an error. But it also means that service won't be rate limited as intended. Double-check the format after saving.

How do I decide what limit to set?
Don't guess. Measure your own environment's traffic first, and set your limit with a safety margin above your observed legitimate peak.

Feedback