SDK version tracking
Track which SDK versions are deployed across your projects and environments to maintain security, compatibility, and access to the latest features.
Why track SDK versions
Tracking SDK versions helps you:
- Identify outdated SDKs that may have security vulnerabilities
- Understand which SDKs are approaching end-of-life
- Plan SDK upgrade initiatives across your organization
- Monitor relay proxy versions alongside client SDKs
- Maintain compliance with security policies
SDK versions API endpoint
LaunchDarkly provides a dedicated API endpoint to retrieve detailed SDK version usage data. The endpoint returns one record per unique SDK and project/environment combination, representing the maximum version observed in the last day.
Endpoint details
Endpoint:
GET https://app.launchdarkly.com/api/v2/usage/sdk-versions/details
Authentication: The endpoint requires an API access token with appropriate permissions. Include the token in the Authorization header.
Beta API header:
This endpoint is currently in beta. Include the LD-API-Version: beta header in your requests:
curl -H "Authorization: api-your-access-token" \
-H "LD-API-Version: beta" \
https://app.launchdarkly.com/api/v2/usage/sdk-versions/details
Data refresh: The endpoint refreshes hourly and returns the maximum version for each SDK observed in the last day.
Response data
The endpoint returns an array of SDK version records. Each record includes:
| Field | Description |
|---|---|
name | SDK name |
version | Observed SDK version |
type | SDK type |
projectId | Project identifier |
projectKey | Project key |
projectName | Project display name |
environmentId | Environment identifier |
environmentKey | Environment key |
environmentName | Environment display name |
applicationId | Application identifier if available |
ldLatestVersion | Latest available minor version from LaunchDarkly (truncated semver, e.g. 9.11). May be empty if the SDK type is unrecognized. The installed version may legitimately exceed this value while still showing EolAllClear. |
eolStatus | End-of-life status. Possible values: EolAllClear (not near or past EOL), EolNear (approaching EOL), EolPast (past end-of-life), MajorVersionAvailable (newer major version available, not near EOL), EolUnknown (no EOL metadata available) |
latestReleaseUrl | URL to latest SDK release |
connectionType | Connection type. Possible values: direct, relay |
relayVersion | Relay proxy version if connected through relay |
relayEolStatus | Relay proxy end-of-life status |
relayLatestVersion | Latest available relay proxy version |
relayLatestReleaseUrl | URL to latest relay proxy release |
Example requests
Python:
import requests
url = "https://app.launchdarkly.com/api/v2/usage/sdk-versions/details"
headers = {
"Authorization": "api-your-access-token",
"LD-API-Version": "beta"
}
response = requests.get(url, headers=headers)
sdk_versions = response.json()
for sdk in sdk_versions:
print(f"{sdk['name']} v{sdk['version']} in {sdk['projectKey']}/{sdk['environmentKey']}")
if sdk.get('eolStatus'):
print(f" EOL Status: {sdk['eolStatus']}")
JavaScript:
const url = 'https://app.launchdarkly.com/api/v2/usage/sdk-versions/details';
const options = {
method: 'GET',
headers: {
'Authorization': 'api-your-access-token',
'LD-API-Version': 'beta'
}
};
async function main() {
const response = await fetch(url, options);
const sdkVersions = await response.json();
sdkVersions.forEach(sdk => {
console.log(`${sdk.name} v${sdk.version} in ${sdk.projectKey}/${sdk.environmentKey}`);
if (sdk.eolStatus) {
console.log(` EOL Status: ${sdk.eolStatus}`);
}
});
}
main().catch(console.error);
Go:
package main
import (
"encoding/json"
"fmt"
"net/http"
"io"
)
type SDKVersion struct {
Name string `json:"name"`
Version string `json:"version"`
ProjectKey string `json:"projectKey"`
EnvironmentKey string `json:"environmentKey"`
EOLStatus string `json:"eolStatus"`
LatestVersion string `json:"ldLatestVersion"`
}
func main() {
url := "https://app.launchdarkly.com/api/v2/usage/sdk-versions/details"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Printf("error creating request: %v\n", err)
return
}
req.Header.Add("Authorization", "api-your-access-token")
req.Header.Add("LD-API-Version", "beta")
res, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Printf("error making request: %v\n", err)
return
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
fmt.Printf("unexpected status: %d\n", res.StatusCode)
return
}
body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Printf("error reading response: %v\n", err)
return
}
var versions []SDKVersion
if err := json.Unmarshal(body, &versions); err != nil {
fmt.Printf("error parsing response: %v\n", err)
return
}
for _, sdk := range versions {
fmt.Printf("%s v%s in %s/%s\n", sdk.Name, sdk.Version, sdk.ProjectKey, sdk.EnvironmentKey)
if sdk.EOLStatus != "" {
fmt.Printf(" EOL Status: %s\n", sdk.EOLStatus)
}
}
}
Use cases
Identify SDKs that need attention
Use eolStatus as the authoritative signal for whether an SDK needs action. LaunchDarkly computes this field server-side, so you do not need to compare version strings yourself.
Avoid comparing version against ldLatestVersion directly. ldLatestVersion uses truncated semver (for example 9.11 rather than 9.11.0), so an installed version of 9.11.2 appears newer even though the SDK is current. More importantly, LaunchDarkly can mark an SDK EolAllClear even when the installed version exceeds ldLatestVersion — the field reflects EOL policy, not a simple version number race. Comparing raw strings produces false positives that create noise and erode trust in your monitoring.
Instead, filter on eolStatus and use latestReleaseUrl to link directly to the release notes:
import requests
from collections import defaultdict
url = "https://app.launchdarkly.com/api/v2/usage/sdk-versions/details"
headers = {
"Authorization": "api-your-access-token",
"LD-API-Version": "beta"
}
# Priority order for upgrade planning
STATUS_PRIORITY = {
'EolPast': 1,
'EolNear': 2,
'MajorVersionAvailable': 3,
}
response = requests.get(url, headers=headers)
sdk_versions = response.json()
# Collect SDKs with actionable statuses
actionable = [
sdk for sdk in sdk_versions
if sdk.get('eolStatus') in STATUS_PRIORITY
]
# Sort by priority, then by SDK name
actionable.sort(key=lambda s: (STATUS_PRIORITY[s['eolStatus']], s['name']))
print(f"Found {len(actionable)} SDK instances needing attention\n")
for sdk in actionable:
print(f" [{sdk['eolStatus']}] {sdk['name']} v{sdk['version']}"
f" in {sdk['projectKey']}/{sdk['environmentKey']}")
if sdk.get('latestReleaseUrl'):
print(f" Release notes: {sdk['latestReleaseUrl']}")
Monitor end-of-life SDKs
Group SDKs by eolStatus to get a clear picture of your upgrade backlog across all five possible values:
import requests
from collections import defaultdict
url = "https://app.launchdarkly.com/api/v2/usage/sdk-versions/details"
headers = {
"Authorization": "api-your-access-token",
"LD-API-Version": "beta"
}
STATUS_LABEL = {
'EolPast': 'Past EOL — upgrade immediately',
'EolNear': 'Approaching EOL — plan upgrade',
'MajorVersionAvailable': 'Major version available — upgrade when ready',
'EolAllClear': 'Healthy',
'EolUnknown': 'No EOL data available',
}
response = requests.get(url, headers=headers)
sdk_versions = response.json()
by_status = defaultdict(list)
for sdk in sdk_versions:
status = sdk.get('eolStatus') or 'EolUnknown'
by_status[status].append(sdk)
for status, label in STATUS_LABEL.items():
sdks = by_status.get(status, [])
print(f"\n{label} ({len(sdks)} instances)")
for sdk in sdks:
print(f" {sdk['name']} v{sdk['version']}"
f" in {sdk['projectKey']}/{sdk['environmentKey']}")
Track relay proxy versions
Monitor relay proxy versions across your infrastructure:
import requests
url = "https://app.launchdarkly.com/api/v2/usage/sdk-versions/details"
headers = {
"Authorization": "api-your-access-token",
"LD-API-Version": "beta"
}
response = requests.get(url, headers=headers)
sdk_versions = response.json()
relay_connections = [
sdk for sdk in sdk_versions
if sdk.get('relayVersion')
]
print(f"Found {len(relay_connections)} SDKs connected through relay proxy")
for sdk in relay_connections:
print(f" {sdk['name']} v{sdk['version']} in {sdk['projectKey']}/{sdk['environmentKey']}")
print(f" Relay: v{sdk['relayVersion']}")
if sdk.get('relayEolStatus'):
print(f" Relay EOL Status: {sdk['relayEolStatus']}")
Generate upgrade reports
Create comprehensive reports for SDK upgrade planning:
import requests
from collections import defaultdict
url = "https://app.launchdarkly.com/api/v2/usage/sdk-versions/details"
headers = {
"Authorization": "api-your-access-token",
"LD-API-Version": "beta"
}
response = requests.get(url, headers=headers)
sdk_versions = response.json()
# Group by SDK name
sdks_by_name = defaultdict(list)
for sdk in sdk_versions:
sdks_by_name[sdk['name']].append(sdk)
# Generate report
print("SDK Upgrade Report")
print("=" * 80)
for sdk_name, instances in sdks_by_name.items():
versions = set(inst['version'] for inst in instances)
latest = instances[0].get('ldLatestVersion', 'Unknown')
print(f"\n{sdk_name}")
print(f" Versions in use: {', '.join(sorted(versions))}")
print(f" Latest available: {latest}")
print(f" Total instances: {len(instances)}")
# Count by environment
envs = defaultdict(int)
for inst in instances:
envs[f"{inst['projectKey']}/{inst['environmentKey']}"] += 1
print(f" Deployments:")
for env, count in sorted(envs.items()):
print(f" {env}: {count}")
Automation
Scheduled monitoring with PagerDuty notifications
Set up scheduled jobs to monitor SDK versions and create PagerDuty events. This example maps each eolStatus value to an appropriate PagerDuty severity, then creates one event per SDK type that needs attention:
eolStatus | PagerDuty severity | Meaning |
|---|---|---|
EolPast | critical | SDK is past end-of-life |
EolNear | warning | SDK is approaching end-of-life |
MajorVersionAvailable | info | Newer major version available, not near EOL |
EolAllClear | no alert | SDK is healthy |
EolUnknown | no alert | No EOL metadata available |
import requests
from collections import defaultdict
# Map eolStatus to PagerDuty severity; omitted statuses generate no alert
EOL_SEVERITY = {
'EolPast': 'critical',
'EolNear': 'warning',
'MajorVersionAvailable': 'info',
}
SEVERITY_RANK = {'critical': 3, 'warning': 2, 'info': 1}
def check_sdk_versions():
"""Check SDK versions and create PagerDuty events for SDKs needing attention"""
url = "https://app.launchdarkly.com/api/v2/usage/sdk-versions/details"
headers = {
"Authorization": "api-your-access-token",
"LD-API-Version": "beta"
}
response = requests.get(url, headers=headers)
sdk_versions = response.json()
issues_by_sdk = defaultdict(list)
for sdk in sdk_versions:
eol_status = sdk.get('eolStatus', '')
severity = EOL_SEVERITY.get(eol_status)
if severity is None:
continue # EolAllClear, EolUnknown, or empty — no alert
issues_by_sdk[sdk['name']].append({
'version': sdk['version'],
'project': sdk['projectKey'],
'environment': sdk['environmentKey'],
'eolStatus': eol_status,
'severity': severity,
})
for sdk_name, issues in issues_by_sdk.items():
create_pagerduty_event(sdk_name, issues)
def create_pagerduty_event(sdk_name, issues):
"""Create a PagerDuty Events API v2 event for a specific SDK type"""
# Use the highest severity across all affected instances
severity = max(issues, key=lambda i: SEVERITY_RANK[i['severity']])['severity']
summary = f"{sdk_name}: {len(issues)} instance(s) need attention"
# Group instances by eolStatus for readable custom details
by_status = defaultdict(list)
for issue in issues[:10]: # Limit to first 10
by_status[issue['eolStatus']].append(
f"{issue['project']}/{issue['environment']}: v{issue['version']}"
)
payload = {
'routing_key': 'your-integration-key-here',
'event_action': 'trigger',
'dedup_key': f'launchdarkly-sdk-{sdk_name.lower().replace(" ", "-")}',
'payload': {
'summary': summary,
'severity': severity,
'source': 'LaunchDarkly SDK Version Monitor',
'custom_details': dict(by_status),
},
'links': [
{
'href': 'https://app.launchdarkly.com',
'text': 'LaunchDarkly Dashboard'
}
]
}
pagerduty_url = 'https://events.pagerduty.com/v2/enqueue'
response = requests.post(pagerduty_url, json=payload)
if response.status_code == 202:
print(f"✓ PagerDuty event created for {sdk_name} (severity: {severity})")
else:
print(f"✗ Failed to create PagerDuty event for {sdk_name}: {response.text}")
# Run this script on a schedule using cron or similar
check_sdk_versions()
Example PagerDuty events created:
When this script runs against an account with SDKs needing attention, it creates separate events:
-
Python Server SDK event (
EolPast):- Summary: “Python Server SDK: 2 instance(s) need attention”
- Severity:
critical - Custom details grouped under
EolPast
-
Go Server SDK event (
EolNear):- Summary: “Go Server SDK: 1 instance(s) need attention”
- Severity:
warning - Custom details grouped under
EolNear
-
React Web SDK event (
MajorVersionAvailable):- Summary: “React Web SDK: 5 instance(s) need attention”
- Severity:
info - Custom details grouped under
MajorVersionAvailable
Key features:
- Deduplication: Uses
dedup_keyper SDK type to prevent alert fatigue - Severity mapping:
EolPast→critical,EolNear→warning,MajorVersionAvailable→info;EolAllClearandEolUnknowngenerate no alert - Highest-severity wins: When one SDK has instances at different statuses, the event uses the most severe level
- Rich context: Custom details group instances by
eolStatusfor clear ownership - Actionable: Each alert focuses on a single SDK type
CI/CD integration
Integrate SDK version checks into your CI/CD pipeline:
# GitHub Actions example
name: Check SDK Versions
on:
schedule:
- cron: '0 9 * * 1' # Weekly on Monday at 9 AM
workflow_dispatch:
jobs:
check-versions:
runs-on: ubuntu-latest
steps:
- name: Check SDK Versions
run: |
response=$(curl -s \
-H "Authorization: ${{ secrets.LD_API_TOKEN }}" \
-H "LD-API-Version: beta" \
https://app.launchdarkly.com/api/v2/usage/sdk-versions/details)
echo "$response" | jq -r '.[] | select(.eolStatus and .eolStatus != "EolAllClear" and .eolStatus != "EolUnknown") |
"\(.name) v\(.version) in \(.projectKey)/\(.environmentKey) - EOL: \(.eolStatus)"'
Real-world examples
The following examples use actual data from a LaunchDarkly account to demonstrate practical SDK version analysis.
Example API response structure
The API returns detailed information about each SDK instance. Here is a sample response showing the data structure:
[
{
"name": "Python Server SDK",
"version": "9.8.0",
"type": "server",
"projectId": "aaaaaaaaaaaaaaaaaaaaaaaa",
"projectKey": "api-backend",
"projectName": "API Backend",
"environmentId": "bbbbbbbbbbbbbbbbbbbbbbbb",
"environmentKey": "production",
"environmentName": "Production",
"applicationId": "api-server-prod",
"ldLatestVersion": "9.11",
"eolStatus": "EolPast",
"latestReleaseUrl": "https://github.com/launchdarkly/python-server-sdk/releases/latest",
"connectionType": "direct",
"relayVersion": "",
"relayEolStatus": "",
"relayLatestVersion": "",
"relayLatestReleaseUrl": ""
},
{
"name": "React Web SDK",
"version": "0.2.4",
"type": "browser",
"projectId": "cccccccccccccccccccccccc",
"projectKey": "customer-portal",
"projectName": "Customer Portal",
"environmentId": "dddddddddddddddddddddddd",
"environmentKey": "production",
"environmentName": "Production",
"applicationId": "react-client-sdk",
"ldLatestVersion": "3.3",
"eolStatus": "MajorVersionAvailable",
"latestReleaseUrl": "https://github.com/launchdarkly/react-client-sdk/releases/latest",
"connectionType": "direct",
"relayVersion": "",
"relayEolStatus": "",
"relayLatestVersion": "",
"relayLatestReleaseUrl": ""
},
{
"name": "Node.js Server SDK",
"version": "9.10.5",
"type": "server",
"projectId": "eeeeeeeeeeeeeeeeeeeeeeee",
"projectKey": "mobile-app",
"projectName": "Mobile Application",
"environmentId": "ffffffffffffffffffffffff",
"environmentKey": "production",
"environmentName": "Production",
"applicationId": "NodeJSClient",
"ldLatestVersion": "9.9",
"eolStatus": "EolAllClear",
"latestReleaseUrl": "https://github.com/launchdarkly/js-core/releases/latest",
"connectionType": "direct",
"relayVersion": "",
"relayEolStatus": "",
"relayLatestVersion": "",
"relayLatestReleaseUrl": ""
}
]
Analyzing relay proxy version distribution
A common scenario is identifying which relay proxy versions are deployed across your infrastructure. This example shows an organization with 41 relay proxy instances distributed across two versions:
import requests
from collections import defaultdict
url = "https://app.launchdarkly.com/api/v2/usage/sdk-versions/details"
headers = {
"Authorization": "api-your-access-token",
"LD-API-Version": "beta"
}
response = requests.get(url, headers=headers)
sdk_versions = response.json()
# Analyze relay proxy distribution
relay_by_version = defaultdict(list)
for sdk in sdk_versions:
if sdk['name'] == 'LDRelay':
relay_by_version[sdk['version']].append({
'project': sdk['projectKey'],
'environment': sdk['environmentKey']
})
# Display results
print("Relay Proxy Version Distribution:")
print("=" * 60)
for version, instances in sorted(relay_by_version.items()):
print(f"\nVersion {version}: {len(instances)} instances")
print(f"Sample deployments:")
for inst in instances[:5]:
print(f" - {inst['project']}/{inst['environment']}")
Sample output:
Relay Proxy Version Distribution:
============================================================
Version 8.17.3: 38 instances
Sample deployments:
- mobile-demo/production
- mobile-demo/test
- stream-service/production
- api-gateway/test
- customer-portal/production
Version 8.17.4: 3 instances
Sample deployments:
- core-platform/production
- core-platform/test
- core-platform/dev
Analysis: This output reveals that 38 relay proxy instances are running version 8.17.3, while only 3 have been upgraded to 8.17.4. This indicates a potential upgrade opportunity to standardize on the newer version.
Identifying version drift across environments
Version drift occurs when different environments run different SDK or relay proxy versions. This can lead to inconsistent behavior and makes troubleshooting difficult.
import requests
from collections import defaultdict
url = "https://app.launchdarkly.com/api/v2/usage/sdk-versions/details"
headers = {
"Authorization": "api-your-access-token",
"LD-API-Version": "beta"
}
response = requests.get(url, headers=headers)
sdk_versions = response.json()
# Track versions per project
project_versions = defaultdict(lambda: defaultdict(set))
for sdk in sdk_versions:
project_versions[sdk['projectKey']][sdk['name']].add(
(sdk['version'], sdk['environmentKey'])
)
# Find projects with version drift
print("Projects with version drift:")
print("=" * 60)
for project, sdks in sorted(project_versions.items()):
for sdk_name, versions in sdks.items():
unique_versions = set(v[0] for v in versions)
if len(unique_versions) > 1:
print(f"\n{project} - {sdk_name}:")
for version in sorted(unique_versions):
envs = [v[1] for v in versions if v[0] == version]
print(f" v{version}: {', '.join(envs)}")
Sample output:
Projects with version drift:
============================================================
mobile-demo - LDRelay:
v8.17.3: production, test
v8.17.4: staging
api-gateway - Node.js Server SDK:
v9.8.1: production
v9.10.2: test, dev
Recommendation: Standardize on a single version across environments within each project to ensure consistent behavior and simplify troubleshooting.
SDK inventory report
Generate a comprehensive report of all SDKs in use across your organization:
import requests
from collections import defaultdict
url = "https://app.launchdarkly.com/api/v2/usage/sdk-versions/details"
headers = {
"Authorization": "api-your-access-token",
"LD-API-Version": "beta"
}
response = requests.get(url, headers=headers)
sdk_versions = response.json()
# Summarize SDK usage
print("SDK Inventory Report")
print("=" * 80)
print(f"\nTotal SDK instances: {len(sdk_versions)}")
print(f"Unique projects: {len(set(s['projectKey'] for s in sdk_versions))}")
print(f"Unique environments: {len(set(s['environmentKey'] for s in sdk_versions))}")
# SDK breakdown
sdk_counts = defaultdict(set)
for sdk in sdk_versions:
sdk_counts[sdk['name']].add(sdk['version'])
print("\nSDK Distribution:")
print("-" * 80)
for sdk_name, versions in sorted(sdk_counts.items()):
count = sum(1 for s in sdk_versions if s['name'] == sdk_name)
print(f"{sdk_name}:")
print(f" Instances: {count}")
print(f" Versions: {', '.join(sorted(versions))}")
# Show EOL status if available
sample = next(s for s in sdk_versions if s['name'] == sdk_name)
if sample.get('eolStatus') and sample['eolStatus'] != 'EolUnknown':
print(f" EOL Status: {sample['eolStatus']}")
print()
# Environment coverage
env_counts = defaultdict(int)
for sdk in sdk_versions:
env_counts[sdk['environmentKey']] += 1
print("\nEnvironment Coverage:")
print("-" * 80)
for env, count in sorted(env_counts.items(), key=lambda x: -x[1]):
print(f"{env}: {count} SDK instances")
Sample output:
SDK Inventory Report
================================================================================
Total SDK instances: 46
Unique projects: 17
Unique environments: 8
SDK Distribution:
--------------------------------------------------------------------------------
.NET Server SDK:
Instances: 1
Versions: 8.11.1
EOL Status: EolAllClear
Go Server SDK:
Instances: 1
Versions: 7.14.6
EOL Status: EolAllClear
LDRelay:
Instances: 41
Versions: 8.17.3, 8.17.4
Node.js Server SDK:
Instances: 2
Versions: 9.10.2
EOL Status: EolAllClear
Python Server SDK:
Instances: 1
Versions: 9.14.1
EOL Status: EolAllClear
Environment Coverage:
--------------------------------------------------------------------------------
production: 19 SDK instances
test: 17 SDK instances
dev: 4 SDK instances
sit: 2 SDK instances
qa: 1 SDK instances
Insights from this data:
- Relay proxy is the most deployed component (41 instances)
- Production and test environments have the highest SDK coverage
- Most server-side SDKs show healthy EOL status
- Version standardization opportunity exists for relay proxy
Finding projects without SDK coverage
Identify projects that may not have SDK instrumentation:
import requests
# Fetch all projects (requires separate API call)
projects_url = "https://app.launchdarkly.com/api/v2/projects"
sdk_url = "https://app.launchdarkly.com/api/v2/usage/sdk-versions/details"
headers = {
"Authorization": "api-your-access-token",
"LD-API-Version": "beta"
}
# Get all projects
projects_response = requests.get(projects_url, headers=headers)
all_projects = set(p['key'] for p in projects_response.json()['items'])
# Get projects with SDK usage
sdk_response = requests.get(sdk_url, headers=headers)
projects_with_sdks = set(s['projectKey'] for s in sdk_response.json())
# Find projects without SDK instrumentation
projects_without_sdks = all_projects - projects_with_sdks
print(f"Projects without active SDK usage: {len(projects_without_sdks)}")
for project in sorted(projects_without_sdks):
print(f" - {project}")
This helps identify projects that may need SDK implementation or where SDKs have not reported telemetry recently.
Best practices
Monitor regularly
Schedule regular checks of SDK versions:
- Run automated checks weekly or monthly
- Alert teams when EOL SDKs are detected
- Track upgrade progress over time
Prioritize upgrades
Focus upgrade efforts based on risk:
- Critical: SDKs past end-of-life with known security issues
- High: SDKs approaching end-of-life
- Medium: SDKs more than two major versions behind
- Low: SDKs one minor version behind
Document versions
Maintain documentation of SDK versions:
- Record which versions are approved for use
- Document known issues with specific versions
- Track upgrade timelines and dependencies
Test upgrades
Test SDK upgrades thoroughly:
- Review release notes for breaking changes
- Test in non-production environments first
- Monitor error rates and performance after upgrading
- Have rollback plans ready
To learn more about SDK implementation best practices, read Preflight Checklist. To learn more about maintaining resilient SDK implementations, read Resilient SDK Implementation.