---
title: "DoveRunner Piracy Guard ✨ Service API Guide"
description: "This document explains how to use the service API to integrate DoveRunner Piracy Guard ✨ with customer's system."
---
> For the complete documentation index, see [llms.txt](/llms.txt).

import { LinkCard } from '@astrojs/starlight/components';
import { Steps } from '@astrojs/starlight/components';
import { Badge } from '@astrojs/starlight/components';
import { Tabs, TabItem } from '@astrojs/starlight/components';
import { Code } from '@astrojs/starlight/components';

import SectionExpander from '../../../../../components/SectionExpander.astro';

import logListResponseExample from '/files/service-api-log-list-response-example.json?url&raw';
import dailyStatisticsResponseExample from '/files/service-api-daily-statistics-response-example.json?url&raw';
import topSuspiciousUsersResponseExample from '/files/service-api-top-suspicious-users-response-example.json?url&raw';

Piracy Guard exposes its data through RESTful Business APIs, making it easy for developers to integrate piracy intelligence directly into their workflows and tools. This guide walks through available endpoints, including the required request parameters and the structure of the response data. With these APIs, you can power anomaly detection and piracy analysis for DRM license activity.

These APIs make it possible to retrieve suspicious behavior logs, monitoring daily activity metrics, and surfacing the most suspicious users on your site for deeper investigation.

:::tip

Piracy Guard currently retains anomaly incidents for up to three months.

:::

## Authentication and Authorization

All Piracy Guard APIs require a valid JWT token in the `Authorization` HTTP request header. The token must be issued with your Service API Key and include the required payload fields.

<LinkCard
  title="DoveRunner Service API Guide - JWT Structure"
  description="Details on creation of a JWT for authentication"
  href="/content-security/service-api-guide/#jwt-structure"
/>

Before calling these APIs, ensure that:

<Steps>

1. **Your account must be granted permission to use the Piracy Guard API**
  <p>Contact DoveRunner support via the Helpdesk to request access.</p>

2. **You include the correct `api_code` for each endpoint in your request**
  <p>The `api_code` is validated against your account's authorized permissions — requests with a missing or unauthorized `api_code` will be rejected.</p>
  <p>For requests, you can statically use `UA003001040100`.</p>

</Steps>

## Language Handling

Certain Piracy Guard REST APIs determine the response language using the following priority: `Accept-Language` header → `lang` query parameter → default `en`. The API first checks the `Accept-Language` header for the client's preferred language. If it is not provided or unsupported, the `lang` query parameter is used instead. When neither is specified, the response defaults to English (`en`).

## Service API

### <Badge text="GET" variant="success" /> Log List

Retrieves a list of anomaly detection logs for a given period.

```
GET /api/v2/drm/piracy-guard
```

#### HTTP Headers

`Authorization` (required)\
Encoded JWT token.  See the [Authentication and Authorization](#authentication-and-authorization) for more details.
___
`Accept-Language`\
Language for report.  See the [Language Handling](#language-handling) section for more details.

#### Query Parameters

`api_code` string (required)\
Fixed code of the API (`UA003001040100`).
___
`site_id` string (required)\
Unique identifier of the service site where the DRM request occurred (four-character alphanumeric code)
___
`from` string\
Start date of search window.  Calendar date based on the [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) standard following the `YYYY-MM-DD` format.
___
`to` string\
End date of search window.  Calendar date based on the [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) standard following the `YYYY-MM-DD` format.  When no date is entered, search within the range of today's date.
___
`time_zone` string (default: `+00:00`)\
UTC offset based on the [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) standard following the `±[hh]:[mm]` format.
___
`search_condition` string\
Keyword search category

:::note
  Currently, the only valid value is `user_id`.
:::
___
`search_keyword` string\
Search value for the defined `search_condition` category
___
`report_violation_primary_code` string\
Filter by violation type.  Possible values are—
<ul>
  <li>`license_farming`</li>
  <li>`bot_automation`</li>
  <li>`token_replay`</li>
  <li>`content_ripping`</li>
  <li>`account_sharing`</li>
</ul>
___
`page_unit` integer (default: `25`, max: `1000`)\
Number of items to include in the response
___
`page_index` integer\
Offset by this many pages, of the size of `page_unit`
___
`lang` string (default: `en`)\
Language for report.  See the [Language Handling](#language-handling) section for more details.

#### Example Request

```bash
curl --L 'https://service.doverunner.com/api/v2/drm/piracy-guard?api_code=UA003001040100&site_id=DEMO&from=2026-01-01&to=2026-01-31' \
--H 'Authorization: Bearer <your_jwt_token>' \
--H 'Accept-Language: en'
```

#### Response

<Tabs>
  <TabItem label="Schema">
    `count` long\
    Total number of records
    ___
    `data` array\
    List of Piracy Guard log entries
    <SectionExpander>
      `data[].anomaly` integer\
      A numeric flag representing the anomaly detection result.  This field carries the same value as `anomaly_status`, but expressed as an integer.
      
      Possible values are—
      | Flag | Meaning |
      | - | - |
      | `0` | Entry is considered `normal` with no detected anomaly |
      | `1` | Entry is considered `anomalous` |
      ___
      `data[].site_id` string\
      Unique identifier of the service site where the DRM request occurred (four-character alphanumeric code)
      ___
      `data[].user_id` string\
      Unique identifier of the end user who generated the DRM license requests
      ___
      `data[].unique_key` string\
      A unique hash key generated from the combination of `user_id` and the aggregation period. Used to distinguish records for the same user across different time windows.
      ___
      `data[].start_date` string\
      The start date of the aggregation period for this log entry.  Formatted as `YYYY-MM-DD` per [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).
      ___
      `data[].end_date` string\
      The end date of the aggregation period for this log entry.  Formatted as `YYYY-MM-DD` per [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).
      ___
      `data[].log_date` string\
      The timestamp at which this log record was generated, in `YYYYMMDDhhmmss` format
      ___
      `data[].license_cnt` integer\
      Total number of DRM license requests made by the user within a 24-hour period. A count significantly exceeding typical viewing patterns may indicate automated mass requests or account sharing.
      ___
      `data[].req_unique_hour_cnt` integer\
      Number of distinct hours (out of 24) during which DRM license requests were made. A high count suggests requests spread throughout the day, which may indicate automated or continuous access rather than normal viewing behavior.
      ___
      `data[].device_id_diversity_score` float\
      A diversity score (`0.0`-`1.0`) reflecting how varied the device IDs are across license requests. `0.0` means all requests came from the same device; `1.0` means every request used a different device ID. Higher values may suggest access from multiple devices, potentially indicating account sharing.
      ___
      `data[].device_model_diversity_score` float\
      A diversity score (`0.0`-`1.0`) measuring the variety of device models (e.g., iPhone 12, Galaxy S21) used across requests. A high score under a single account may point to abnormal access patterns.
      ___
      `data[].device_type_diversity_score` float\
      A diversity score (`0.0`-`1.0`) reflecting the variety of device types (e.g., Phone, Tablet, TV, Mac) used. A high score from a single user may suggest simultaneous access across multiple device categories, consistent with account sharing.
      ___
      `data[].device_category_unique_cnt` integer\
      Number of distinct device categories (PC, Mobile, TV, Others) observed across requests in a 24-hour period. The maximum value is 4. A higher count suggests access from a broader range of device environments.
      ___
      `data[].device_category_pc_ratio` float\
      Proportion of DRM license requests originating from PC devices, ranging from `0.0` to `1.0`
      ___
      `data[].device_category_mobile_ratio` float\
      Proportion of DRM license requests originating from mobile devices (smartphones, tablets, etc.), ranging from `0.0` to `1.0`
      ___
      `data[].device_category_tv_ratio` float\
      Proportion of DRM license requests originating from TV devices, ranging from `0.0` to `1.0`
      ___
      `data[].device_category_others_ratio` float\
      Proportion of DRM license requests originating from device types not classified as PC, Mobile, or TV, ranging from `0.0` to `1.0`
      ___
      `data[].drm_type_unique_cnt` integer\
      Number of distinct DRM types (Widevine, FairPlay, PlayReady) used within a 24-hour period. Since different platforms use different DRM systems (e.g., iOS uses FairPlay, Android uses Widevine), multiple DRM types can reflect legitimate multi-device usage.
      ___
      `data[].drm_type_widevine_ratio` float\
      Proportion of license requests using Widevine DRM, ranging from `0.0` to `1.0`
      ___
      `data[].drm_type_fairplay_ratio` float\
      Proportion of license requests using FairPlay DRM, ranging from `0.0` to `1.0`
      ___
      `data[].drm_type_playready_ratio` float\
      Proportion of license requests using PlayReady DRM, ranging from `0.0` to `1.0`
      ___
      `data[].os_type_unique_cnt` integer\
      Number of distinct operating systems observed across DRM license requests in a 24-hour period. A value of 3 (e.g., iOS, Android, Windows) is not inherently abnormal and may simply reflect legitimate multi-device usage.
      ___
      `data[].os_type_ios_ratio` float\
      Proportion of DRM license requests originating from iOS devices, ranging from `0.0` to `1.0`
      ___
      `data[].os_type_android_ratio` float\
      Proportion of DRM license requests originating from Android devices, ranging from `0.0` to `1.0`
      ___
      `data[].os_type_mac_ratio` float\
      Proportion of DRM license requests originating from macOS devices, ranging from `0.0` to `1.0`
      ___
      `data[].os_type_windows_ratio` float\
      Proportion of DRM license requests originating from Windows devices, ranging from `0.0` to `1.0`
      ___
      `data[].os_type_web_ratio` float\
      Proportion of DRM license requests originating from web browser environments (web runtime), ranging from `0.0` to `1.0`
      ___
      `data[].os_type_others_ratio` float\
      Proportion of DRM license requests from operating systems not classified as iOS, Android, macOS, Windows, or Web, ranging from `0.0` to `1.0`
      ___
      `data[].region_unique_cnt` integer\
      Number of distinct AWS regions from which DRM license requests originated (e.g., Seoul, Frankfurt). A higher count indicates geographically distributed access, which may be a sign of account sharing via VPN or proxy.
      ___
      `data[].cid_diversity_score` float\
      A diversity score (`0.0`-`1.0`) reflecting the variety of Content IDs requested. `0.0` means all requests were for the same content; `1.0` means every request targeted a different Content ID. A low score combined with a high request count may suggest repeated license harvesting for specific content.
      ___
      `data[].scheme_unique_cnt` integer\
      Number of distinct URI schemes (e.g., http, https) used in DRM license request URLs. The maximum value is 2. Generally, the value should be 1 for it to be normal. 
      ___
      `data[].path_unique_cnt` integer\
      Number of unique URL paths sent to the DoveRunner Multi-DRM license server within a 24-hour period. A higher count indicates access to a wider range of license endpoints.
      ___
      `data[].ip_pattern_unique_cnt` integer\
      Number of unique IP hop patterns observed in the network paths of DRM license requests (e.g., a 4-hop path and a 5-hop path count as 2 distinct patterns). A higher count suggests access from multiple network environments or routing through proxies.
      ___
      `data[].security_level_unique_cnt` integer\
      Number of distinct DRM security levels observed across license requests in a 24-hour period. Each DRM type defines its own security tiers (e.g., Widevine L1/L3, PlayReady SL150/SL3000). A higher count may indicate access from multiple device types or environments under the same account.
      ___
      `data[].security_policy_diversity_score` float\
      A diversity score (`0.0`-`1.0`) reflecting the variety of DRM security policy configurations applied across requests. A value near `0.0` is common in services with standardized policies and is not, by itself, an indicator of abnormal behavior.
      ___
      `data[].playback_policy_diversity_score` float\
      A diversity score (`0.0`-`1.0`) reflecting the variety of DRM playback policy configurations applied across requests. Similar to security policy, a low score is expected when the service applies standardized playback policies across all content and users.
      ___
      `data[].token_avg_time_diff` float\
      Average time (in seconds) between DRM license token creation and the actual license request. In normal client behavior, this gap is typically a few seconds due to sequential processing. A value significantly above 10 seconds may indicate delayed reuse, consistent with token harvesting or replay attacks.
      ___
      `data[].req_avg_time_diff` float\
      Average time (in seconds) between consecutive DRM license requests from the same user within a 24-hour period. Typical values fall around 5,000-6,000 seconds. Very short intervals may indicate bots or automated mass requests, although legitimately browsing or sampling multiple content items can also produce short gaps.
      ___
      `data[].hash_duplication_score` float\
      A duplication score (`0.0`-`1.0`) measuring how often the same license token hash appears across requests. `0.0` means all hashes are unique; `1.0` means all requests used the same hash. Any value above `0.0` indicates token reuse, and higher values suggest a greater likelihood of token replay or license farming. Hash values are based on the [Hash Message Format](https://docs.doverunner.com/content-security/multi-drm/license/license-token/#sha256-hash-message-format).
      ___
      `data[].session_id_duplication_score` float\
      A duplication score (`0.0`-`1.0`) measuring the degree to which session IDs are reused across DRM license requests. `0.0` means all session IDs are unique; `1.0` means a single session ID was used for all requests. Higher values may indicate session reuse, consistent with automated access or session hijacking.
      ___
      `data[].anomaly_status` string\
      A human-readable string representing the anomaly detection result. `normal` indicates no anomaly was detected; `abnormal` indicates the entry was flagged. This field carries the same value as the `anomaly` flag, expressed in a more readable format.
      ___
      `data[].report_incident_report` string\
      A detailed incident summary generated from variable fact evidence generated by Piracy Guard. Explicitly references `license_cnt` and explains why the observed behavior deviates from normal patterns — for example, `avg_delay_between_requests` being far below the typical 5,000-6,000s range, multiple DRM types observed in a short window, or elevated `region_unique_cnt`.
      ___
      `data[].report_reasoning` string\
      A list of key reasons behind the anomaly determination. Each element references a specific feature from the user statistics — such as abnormally high `license_cnt`, shortened `avg_delay_between_requests`, or elevated `device_id_diversity_score` — and explains its significance. Provided as a JSON array encoded as a string.
      ___
      `data[].report_risk_level` string\
      The overall risk level of the detected anomalous activity. Possible values are `high`, `medium`, or `low`.
      ___
      `data[].report_violation_notes` string\
      A concise technical note linking quantitative evidence to the identified violation type. Written by directly citing actual feature names and values from the input, for example:
      ```txt
      avg_delay_between_requests=35s (bucketed to 40s), session_id_duplication_score=0.80, region_unique_cnt=3.
      ```
      ___
      `data[].report_violation_primary_code` string\
      The code identifying the most likely violation type. Possible values are—
      <ul>
        <li>`license_farming`</li>
        <li>`bot_automation`</li>
        <li>`token_replay`</li>
        <li>`content_ripping`</li>
        <li>`account_sharing`</li>
      </ul>
      ___
      `data[].report_violation_primary` string\
      A JSON object (encoded as a string) containing detailed information about the primary violation type. Includes the violation code (`code`), English description (`en`), Korean description (`ko`), and confidence score (`confidence`). When no anomaly is detected, `code` is set to `none` and confidence to `0.0`.
      ___
      `data[].report_violation_primary_confidence` float\
      A confidence score (`0.0`-`1.0`) representing the model's certainty in the primary violation classification. A higher value indicates greater confidence in the assigned violation type. This value is set to `0.0` when no anomaly is detected.
    </SectionExpander>
  </TabItem>
  <TabItem label="Example">
    <Code
      lang="json"
      title="application/json"
      code={logListResponseExample}
    />
  </TabItem>
</Tabs>

### <Badge text="GET" variant="success" /> Daily Statistics

Retrieves a list of daily suspicious activity statistics for the specified month.  If requesting for the *current* month, results returned will include items from the first of the *current* month through *yesterday*.

```
GET /api/v2/drm/piracy-guard/statistics
```

#### HTTP Headers

`Authorization` (required)\
Encoded JWT token.  See the [Authentication and Authorization](#authentication-and-authorization) for more details.

#### Query Parameters

`api_code` string (required)\
Fixed code of the API (`UA003001040100`)
___
`site_id` string (required)\
Unique identifier of the service site where the DRM request occurred (four-character alphanumeric code)
___
`target_date` string  (required)\
Reference date.  Calendar date based on the [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) standard following the `YYYY-MM-DD` format.

:::caution

The day value is ignored — statistics for the entire month are returned.

:::

#### Example Request

```bash
curl --L 'https://service.doverunner.com/api/v2/drm/piracy-guard/statistics?api_code=UA003001040100&site_id=DEMO&target_date=2025-11-01' \
--H 'Authorization: Bearer <your_jwt_token>'
```

#### Response

<Tabs>
  <TabItem label="Schema">
    `count` long\
    Total number of records
    ___
    `data` array\
    List of Piracy Guard daily statistics entries
    <SectionExpander>
      `data[].target_date` integer\
      Target day of the month
      ___
      `data[].site_suspicious_cnt` long\
      Daily suspicious activity count
      ___
      `data[].risk_level_distribution` object\
      Daily distribution by risk level.  Keys with a zero value are not included in the response data.
      <SectionExpander>
        `data[].risk_level_distribution.high` integer\
        Violations that were marked as high risk
        ___
        `data[].risk_level_distribution.medium` integer\
        Violations that were marked as medium risk
        ___
        `data[].risk_level_distribution.low` integer\
        Violations that were marked as low risk
      </SectionExpander>
      ___
      `data[].violation_type_distribution` object\
      Daily distribution by violation type.  Keys with a zero value are not included in the response data.
      <SectionExpander>
        `data[].risk_level_distribution.license_farming` integer\
        Count of occurrences of `license_farming` violations
        ___
        `data[].risk_level_distribution.bot_automation` integer\
        Count of occurrences of `bot_automation` violations
        ___
        `data[].risk_level_distribution.token_replay` integer\
        Count of occurrences of `token_replay` violations
        ___
        `data[].risk_level_distribution.content_ripping` integer\
        Count of occurrences of `content_ripping` violations
        ___
        `data[].risk_level_distribution.account_sharing` integer\
        Count of occurrences of `account_sharing` violations
      </SectionExpander>
      ___
      `data[].site_suspicious_acc_cnt` long\
      Monthly cumulative suspicious activity count
      ___
      `data[].risk_level_acc_distribution` object\
      Monthly cumulative distribution by risk level.  Keys with a zero value are not included in the response data.
      <SectionExpander>
        `data[].risk_level_distribution.high` integer\
        Violations that were marked as high risk
        ___
        `data[].risk_level_distribution.medium` integer\
        Violations that were marked as medium risk
        ___
        `data[].risk_level_distribution.low` integer\
        Violations that were marked as low risk
      </SectionExpander>
      ___
      `data[].violation_type_acc_distribution` object\
      Monthly cumulative distribution by violation type.  Keys with a zero value are not included in the response data.
      <SectionExpander>
        `data[].violation_type_acc_distribution.license_farming` integer\
        Count of occurrences of `license_farming` violations
        ___
        `data[].violation_type_acc_distribution.bot_automation` integer\
        Count of occurrences of `bot_automation` violations
        ___
        `data[].violation_type_acc_distribution.token_replay` integer\
        Count of occurrences of `token_replay` violations
        ___
        `data[].violation_type_acc_distribution.content_ripping` integer\
        Count of occurrences of `content_ripping` violations
        ___
        `data[].violation_type_acc_distribution.account_sharing` integer\
        Count of occurrences of `account_sharing` violations
      </SectionExpander>
    </SectionExpander>
  </TabItem>
  <TabItem label="Example">
    <Code
      lang="json"
      title="application/json"
      code={dailyStatisticsResponseExample}
    />
  </TabItem>
</Tabs>

### <Badge text="GET" variant="success" /> Top Suspicious Users

Retrieves the top users ranked by monthly suspicious activity count.

```
GET /api/v2/drm/piracy-guard/users/top
```

#### HTTP Headers

`Authorization` (required)\
Encoded JWT token.  See the [Authentication and Authorization](#authentication-and-authorization) for more details.

#### Query Parameters

`api_code` string (required)\
Fixed code of the API (`UA003001040100`)
___
`site_id` string (required)\
Unique identifier of the service site where the DRM request occurred (four-character alphanumeric code)
___
`target_date` string  (required)\
Reference date.  Calendar date based on the [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) standard following the `YYYY-MM-DD` format.

#### Example Request

```bash
curl --L 'https://service.doverunner.com/api/v2/drm/piracy-guard/users/top?api_code=UA003001040100&site_id=DEMO&target_date=2025-11-01' \
--H 'Authorization: Bearer <your_jwt_token>'
```

#### Response

<Tabs>
  <TabItem label="Schema">
    `count` long\
    Total number of records
    ___
    `data` array\
    List of monthly top suspicious users
    <SectionExpander>
      `data[].user_id` string\
      Unique identifier of the end user who generated the DRM license requests
      ___
      `data[].site_suspicious_cnt` long\
      Number of suspicious activity occurrences
    </SectionExpander>
  </TabItem>
  <TabItem label="Example">
    <Code
      lang="json"
      title="application/json"
      code={topSuspiciousUsersResponseExample}
    />
  </TabItem>
</Tabs>