> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.pixalate.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.pixalate.com/_mcp/server.

# Resolve named identifiers to the best-matching program

POST https://epg-api.pixalate.com/v1/lookup
Content-Type: application/json

The flat REST path: one JSON object of named identifier parameters in, a
diagnostic envelope out. **Each additional identifier parameter you provide
increases the precision of the results**: `bundle` alone fully resolves
single-channel apps but returns a structured miss on aggregator apps; adding
`channelId` or `channelName` resolves the channel and joins the schedule at the
lookup instant; adding `title` resolves the program directly. One
uniform response shape covers every outcome — parts are populated or empty, never
reshaped.

Unlike the passthrough route, this route returns confidence and per-candidate
diagnostics in the body — use it for impression-log enrichment, DSP-side lookups,
and integration debugging. Both routes honor a supplied instant; this one reads it
from the flat `timestamp` field below. Pass `verbose: true` during integration
against an environment that offers the debug object: it shows exactly how each
input was read. The sandbox at `dev-epg-api.pixalate.com` offers it.


Reference: https://docs.pixalate.com/api-reference/openepg/lookup

## Authentication

- `x-api-key` header (required) — Your Pixalate-provided API key, in the request header on every route. Credentials are never accepted in query parameters. An API key may be obtained by signing up for an account on the [Pixalate developer website](https://developer.pixalate.com).
- `Authorization` header (basic auth, required) — Username/password alternative to the API-key header. Headers only — credentials are never accepted in query parameters.

## Servers

- `https://epg-api.pixalate.com/v1` (Production., default)
- `https://dev-epg-api.pixalate.com/v1` (Development sandbox.)

## Request

### Body (application/json)

This endpoint expects a LookupRequest.

- `bundle` (string, required) — Required. App store bundle/ID. Alone it fully resolves single-channel apps; on an aggregator (multi-channel) app with no channel or show identifier alongside it, the result is a structured CHANNEL_UNRESOLVED miss, never a guess.
- `channelId` (string, optional) — Native/platform channel identifier, matched exactly against known identifiers (substring containment as a fallback). Strictly supersedes channelName — when present, the name is not consulted.
- `channelName` (string, optional) — Channel display name (fuzzy name match). Consulted only when channelId is absent. Capped at 256 characters; longer values are truncated.
- `title` (string, optional) — Show/program title. Fuzzy match, scoped to the declared app's catalog; a resolved show always carries its channel association. When the channel path and the show path disagree, the channel + timestamp result wins and this input is marked CONFLICT in verbose debug. Capped at 256 characters; longer values are truncated.
- `os` (enum, optional) — Platform/OS, from a fixed enumerated vocabulary. An out-of-vocabulary value, including a different letter case, → 400 — never silently degraded. Omit the field for a platform not listed; resolution then runs against all platforms.
  - Allowed values: `roku`, `fireos`, `android`, `samsung`, `webos`, `tvos`
- `devicetype` (integer, optional) — Optional OpenRTB device type. Accepted and retained for forward compatibility; not consulted by resolution during the beta.
- `timestamp` (datetime, optional) — ISO 8601 UTC lookup instant; absent means now. Per item in batch, so an impression log enriches against the schedule as of each impression. Up to 5 minutes in the future clamps to now (TIMESTAMP_CLAMPED notice); further in the future, older than the 7-day window, or unparseable → 400.
- `verbose` (boolean, optional, default: false) — Ask for the debug object in the response. Whether the request is answered with one is a property of the environment: an environment that does not offer the debug object returns the ordinary envelope without it, and with nothing on the wire to say so. The sandbox at `dev-epg-api.pixalate.com` offers it.

## Response

### 200

Lookup performed (hit or structured miss).

- `notices` (list of enum, required)
  - Allowed values: `TIMESTAMP_CLAMPED`
- `candidates` (list of ContentCandidate, required) — The best match on a hit; empty on a miss.
- `reason` (enum, optional) — Present when candidates is empty or degraded. Single-valued — the earliest resolution stage that failed; a stage that never ran doesn't count. Finer-grained internal subcategories are never serialized in any response. | Value | Meaning | What to do | | --- | --- | --- | | `APP_UNRESOLVED` | The bundle did not resolve to a supported app. | Confirm the bundle is a CTV app in scope. | | `OUT_OF_SCOPE` | The app resolved and is not an EPG app — enrichment does not apply to it (e.g. a game). | Nothing — enrichment is not applicable. | | `NO_COVERAGE` | The app resolved and is an EPG app, but the EPG data needed to answer is not held — no lineup for it, or no schedule covering the instant. | Treat the impression as unenriched. Not billable. | | `CHANNEL_UNRESOLVED` | Channel-bearing inputs produced no channel on this app — including an app-only request on an aggregator app. | Check the identifier is correct and belongs to the declared app; pass a channel id or name on aggregator traffic. | | `SHOW_UNRESOLVED` | The show input produced no content on this app. | Check the title; pass a channel identifier alongside it. |
  - Allowed values: `APP_UNRESOLVED`, `OUT_OF_SCOPE`, `NO_COVERAGE`, `CHANNEL_UNRESOLVED`, `SHOW_UNRESOLVED`
- `debug` (Debug, optional) — Present on the parameterized lookup route when verbose=true and the environment offers the debug object. It is an integration aid the sandbox at `dev-epg-api.pixalate.com` offers, not a guarantee of every environment: an environment that does not offer it answers a verbose request with the ordinary envelope and no debug object, and with no notice, reason code, or error to say so. Identifier-level disclosures (supersession, ignored-empty, unsupported, conflicts) live here and only here. Use it during integration: it shows exactly how each input was read.
- `error` (Error, optional) — Batch only — populated in a slot whose item was invalid (echoes what the single-request 400 would have said).

## Errors

### 400 Bad Request Error

Malformed request — a non-conforming body, a missing or empty required field, a read field of the wrong type, a timestamp violation (unparseable, more than 5 minutes in the future, or older than the 7-day window), an out-of-vocabulary `os`, or an exceeded batch cap.

- `code` (enum, required) — Machine-readable error code. A code names a product condition the HTTP status cannot express; where there is no such distinction the code is the status name, so branch on the status first and on the code only for the values below. RATE_LIMITED is a 429 from a request-rate ceiling and clears within the window. BATCH_TOO_LARGE means the batch exceeded the documented item cap — split and retry. TIMESTAMP_OUT_OF_WINDOW means the instant requested lies outside the 7-day coverage window or more than 5 minutes in the future.
  - Allowed values: `BAD_REQUEST`, `BATCH_TOO_LARGE`, `TIMESTAMP_OUT_OF_WINDOW`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `PAYLOAD_TOO_LARGE`, `RATE_LIMITED`, `SERVICE_UNAVAILABLE`
- `message` (string, required)

### 401 Unauthorized Error

Missing or unknown credential.

- `code` (enum, required) — Machine-readable error code. A code names a product condition the HTTP status cannot express; where there is no such distinction the code is the status name, so branch on the status first and on the code only for the values below. RATE_LIMITED is a 429 from a request-rate ceiling and clears within the window. BATCH_TOO_LARGE means the batch exceeded the documented item cap — split and retry. TIMESTAMP_OUT_OF_WINDOW means the instant requested lies outside the 7-day coverage window or more than 5 minutes in the future.
  - Allowed values: `BAD_REQUEST`, `BATCH_TOO_LARGE`, `TIMESTAMP_OUT_OF_WINDOW`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `PAYLOAD_TOO_LARGE`, `RATE_LIMITED`, `SERVICE_UNAVAILABLE`
- `message` (string, required)

### 403 Forbidden Error

Known credential without entitlement to this product.

- `code` (enum, required) — Machine-readable error code. A code names a product condition the HTTP status cannot express; where there is no such distinction the code is the status name, so branch on the status first and on the code only for the values below. RATE_LIMITED is a 429 from a request-rate ceiling and clears within the window. BATCH_TOO_LARGE means the batch exceeded the documented item cap — split and retry. TIMESTAMP_OUT_OF_WINDOW means the instant requested lies outside the 7-day coverage window or more than 5 minutes in the future.
  - Allowed values: `BAD_REQUEST`, `BATCH_TOO_LARGE`, `TIMESTAMP_OUT_OF_WINDOW`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `PAYLOAD_TOO_LARGE`, `RATE_LIMITED`, `SERVICE_UNAVAILABLE`
- `message` (string, required)

### 413 Content Too Large Error

The request body exceeded the maximum number of bytes this route accepts. Applies to the single (non-batch) operations; well below what a real payload from this route ever produces, so it signals a malformed or abusive request rather than an ordinary large one.

- `code` (enum, required) — Machine-readable error code. A code names a product condition the HTTP status cannot express; where there is no such distinction the code is the status name, so branch on the status first and on the code only for the values below. RATE_LIMITED is a 429 from a request-rate ceiling and clears within the window. BATCH_TOO_LARGE means the batch exceeded the documented item cap — split and retry. TIMESTAMP_OUT_OF_WINDOW means the instant requested lies outside the 7-day coverage window or more than 5 minutes in the future.
  - Allowed values: `BAD_REQUEST`, `BATCH_TOO_LARGE`, `TIMESTAMP_OUT_OF_WINDOW`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `PAYLOAD_TOO_LARGE`, `RATE_LIMITED`, `SERVICE_UNAVAILABLE`
- `message` (string, required)

### 429 Too Many Requests Error

Rate limit exceeded. `Retry-After` says when the window clears.

- `code` (enum, required) — Machine-readable error code. A code names a product condition the HTTP status cannot express; where there is no such distinction the code is the status name, so branch on the status first and on the code only for the values below. RATE_LIMITED is a 429 from a request-rate ceiling and clears within the window. BATCH_TOO_LARGE means the batch exceeded the documented item cap — split and retry. TIMESTAMP_OUT_OF_WINDOW means the instant requested lies outside the 7-day coverage window or more than 5 minutes in the future.
  - Allowed values: `BAD_REQUEST`, `BATCH_TOO_LARGE`, `TIMESTAMP_OUT_OF_WINDOW`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `PAYLOAD_TOO_LARGE`, `RATE_LIMITED`, `SERVICE_UNAVAILABLE`
- `message` (string, required)

### 503 Service Unavailable Error

Serving infrastructure degraded — the API fails closed and never masquerades as a data miss. Retry after the indicated delay.

- `code` (enum, required) — Machine-readable error code. A code names a product condition the HTTP status cannot express; where there is no such distinction the code is the status name, so branch on the status first and on the code only for the values below. RATE_LIMITED is a 429 from a request-rate ceiling and clears within the window. BATCH_TOO_LARGE means the batch exceeded the documented item cap — split and retry. TIMESTAMP_OUT_OF_WINDOW means the instant requested lies outside the 7-day coverage window or more than 5 minutes in the future.
  - Allowed values: `BAD_REQUEST`, `BATCH_TOO_LARGE`, `TIMESTAMP_OUT_OF_WINDOW`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `PAYLOAD_TOO_LARGE`, `RATE_LIMITED`, `SERVICE_UNAVAILABLE`
- `message` (string, required)

## Types

### ContentCandidate

- `content` (Content, required) — OpenRTB 2.6 Content object — the splice boundary. What an SSP forwards downstream is exactly this object; confidence, reasons, and notices live outside it and never enter the bidstream unless you put them there. Fields are populated when known; absent means absent — the API never explains missing metadata. Additional OpenRTB fields permitted. Validated against the OpenRTB 2.6 Content definition (§3.2.16) before serialization.
- `confidence` (double, required) — How sure the service is of this candidate, and the whole of what the service says about the strength of a match. There is no confidence floor — weak matches return with honestly low scores, and you choose the threshold that fits your use case. Airings are judged on the [start, stop) interval; a timestamp exactly at a boundary does not reduce confidence.
- `candidateReason` (enum, optional) — Candidate-level degradation — the channel resolved but nothing airs at the lookup instant; content carries channel identity only. The response-level reason reads NO_COVERAGE alongside.
  - Allowed values: `SCHEDULE_GAP`

### Debug

Present on the parameterized lookup route when verbose=true and the environment offers the debug object. It is an integration aid the sandbox at `dev-epg-api.pixalate.com` offers, not a guarantee of every environment: an environment that does not offer it answers a verbose request with the ordinary envelope and no debug object, and with no notice, reason code, or error to say so. Identifier-level disclosures (supersession, ignored-empty, unsupported, conflicts) live here and only here. Use it during integration: it shows exactly how each input was read.

- `interpreted` (map from string to any, optional) — The request as understood: the platform it resolved against, and the parsed timestamp.
- `inputs` (map from string to DebugInputs, optional) — Verdict map for the evidence-bearing inputs, keyed by request field name: channelId, channelName and title. These are the inputs whose contribution to the result is otherwise invisible. The context inputs — bundle, os and timestamp — carry no verdict because they are not matched against anything; how they were read is reported under `interpreted`.

### Error

- `code` (enum, required) — Machine-readable error code. A code names a product condition the HTTP status cannot express; where there is no such distinction the code is the status name, so branch on the status first and on the code only for the values below. RATE_LIMITED is a 429 from a request-rate ceiling and clears within the window. BATCH_TOO_LARGE means the batch exceeded the documented item cap — split and retry. TIMESTAMP_OUT_OF_WINDOW means the instant requested lies outside the 7-day coverage window or more than 5 minutes in the future.
  - Allowed values: `BAD_REQUEST`, `BATCH_TOO_LARGE`, `TIMESTAMP_OUT_OF_WINDOW`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `PAYLOAD_TOO_LARGE`, `RATE_LIMITED`, `SERVICE_UNAVAILABLE`
- `message` (string, required)

### Content

OpenRTB 2.6 Content object — the splice boundary. What an SSP forwards downstream is exactly this object; confidence, reasons, and notices live outside it and never enter the bidstream unless you put them there. Fields are populated when known; absent means absent — the API never explains missing metadata. Additional OpenRTB fields permitted. Validated against the OpenRTB 2.6 Content definition (§3.2.16) before serialization.

- `id` (string, optional) — OpenEPG content ID for the resolved program. Provisional during the beta, with no stability promise; a stable public identifier scheme arrives at GA.
- `title` (string, optional) — Program title, in display form.
- `series` (string, optional) — Guide-provided series name (not an OpenEPG entity ID).
- `episode` (integer, optional)
- `season` (string, optional) — OpenRTB season. Part of the program-identity cluster a passthrough hit replaces, and never populated during the beta, so a hit always removes it from the request.
- `cat` (list of string, optional) — OpenRTB content categories. Part of the program-identity cluster a passthrough hit replaces, and never populated during the beta, so a hit always removes it from the request.
- `genre` (string, optional) — Genre string, conformant to the TMDB genre taxonomy.
- `contentrating` (string, optional) — Content rating (e.g. TV-PG).
- `livestream` (enum, optional) — Live flag.
  - Allowed values: `0`, `1`
- `len` (integer, optional) — Duration in seconds.
- `language` (string, optional)
- `userrating` (string, optional) — OpenRTB user rating. Identity-neutral: never set by resolution, and preserved from the request on the passthrough route.
- `prodq` (integer, optional) — OpenRTB production quality. Identity-neutral: never set by resolution, and preserved from the request on the passthrough route.
- `channel` (ContentChannel, optional)
- `ext` (ContentExt, optional) — The only non-2.6 data inside content — carries only what should travel downstream. Confidence, reasons, and notices deliberately live OUTSIDE this object.

### DebugInputs

- `verdict` (enum, optional)
  - Allowed values: `MATCHED`, `UNMATCHED`, `IGNORED_EMPTY`, `IGNORED_SUPERSEDED`, `UNSUPPORTED`, `CONFLICT`
- `hint` (string, optional) — Optional diagnostic — for example, a value that appears to belong in a different field.

### ContentChannel

- `id` (string, optional) — OpenEPG channel ID. Provisional during the beta, with no stability promise; a stable public identifier scheme arrives at GA.
- `name` (string, optional) — Channel display name (e.g. "ESPN", not a normalized matching form).

### ContentExt

The only non-2.6 data inside content — carries only what should travel downstream. Confidence, reasons, and notices deliberately live OUTSIDE this object.

- `openepg` (ContentExtOpenepg, optional)

### ContentExtOpenepg

- `attribution` (string, optional) — Vendor attribution.

## Examples

### App only on an aggregator: structured miss, never a guess

**Response**

```json
{
  "notices": [],
  "candidates": [],
  "reason": "CHANNEL_UNRESOLVED"
}
```

**SDK Code**

```python App only on an aggregator: structured miss, never a guess
import requests

url = "https://epg-api.pixalate.com/v1/lookup"

headers = {"x-api-key": "<apiKey>"}

response = requests.post(url, headers=headers)

print(response.json())
```

```javascript App only on an aggregator: structured miss, never a guess
const url = 'https://epg-api.pixalate.com/v1/lookup';
const options = {method: 'POST', headers: {'x-api-key': '<apiKey>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go App only on an aggregator: structured miss, never a guess
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://epg-api.pixalate.com/v1/lookup"

	req, _ := http.NewRequest("POST", url, nil)

	req.Header.Add("x-api-key", "<apiKey>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby App only on an aggregator: structured miss, never a guess
require 'uri'
require 'net/http'

url = URI("https://epg-api.pixalate.com/v1/lookup")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<apiKey>'

response = http.request(request)
puts response.read_body
```

```java App only on an aggregator: structured miss, never a guess
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://epg-api.pixalate.com/v1/lookup")
  .header("x-api-key", "<apiKey>")
  .asString();
```

```php App only on an aggregator: structured miss, never a guess
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://epg-api.pixalate.com/v1/lookup', [
  'headers' => [
    'x-api-key' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp App only on an aggregator: structured miss, never a guess
using RestSharp;

var client = new RestClient("https://epg-api.pixalate.com/v1/lookup");
var request = new RestRequest(Method.POST);
request.AddHeader("x-api-key", "<apiKey>");
IRestResponse response = client.Execute(request);
```

```swift App only on an aggregator: structured miss, never a guess
import Foundation

let headers = ["x-api-key": "<apiKey>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://epg-api.pixalate.com/v1/lookup")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### With a channel identifier: deterministic hit

**Response**

```json
{
  "notices": [],
  "candidates": [
    {
      "content": {
        "id": "ec-4Fq0X2",
        "title": "SportsCenter",
        "genre": "Sports",
        "contentrating": "TV-PG",
        "channel": {
          "id": "eg-Tj5r8w",
          "name": "ESPN"
        },
        "ext": {
          "openepg": {
            "attribution": "pixalate-openepg/v1"
          }
        }
      },
      "confidence": 1
    }
  ]
}
```

**SDK Code**

```python With a channel identifier: deterministic hit
import requests

url = "https://epg-api.pixalate.com/v1/lookup"

headers = {"x-api-key": "<apiKey>"}

response = requests.post(url, headers=headers)

print(response.json())
```

```javascript With a channel identifier: deterministic hit
const url = 'https://epg-api.pixalate.com/v1/lookup';
const options = {method: 'POST', headers: {'x-api-key': '<apiKey>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go With a channel identifier: deterministic hit
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://epg-api.pixalate.com/v1/lookup"

	req, _ := http.NewRequest("POST", url, nil)

	req.Header.Add("x-api-key", "<apiKey>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby With a channel identifier: deterministic hit
require 'uri'
require 'net/http'

url = URI("https://epg-api.pixalate.com/v1/lookup")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<apiKey>'

response = http.request(request)
puts response.read_body
```

```java With a channel identifier: deterministic hit
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://epg-api.pixalate.com/v1/lookup")
  .header("x-api-key", "<apiKey>")
  .asString();
```

```php With a channel identifier: deterministic hit
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://epg-api.pixalate.com/v1/lookup', [
  'headers' => [
    'x-api-key' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp With a channel identifier: deterministic hit
using RestSharp;

var client = new RestClient("https://epg-api.pixalate.com/v1/lookup");
var request = new RestRequest(Method.POST);
request.AddHeader("x-api-key", "<apiKey>");
IRestResponse response = client.Execute(request);
```

```swift With a channel identifier: deterministic hit
import Foundation

let headers = ["x-api-key": "<apiKey>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://epg-api.pixalate.com/v1/lookup")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### App only, on an aggregator app

**Request**

```json
{
  "bundle": "com.pluto.tv"
}
```

**Response**

```json
{
  "notices": [],
  "candidates": [],
  "reason": "CHANNEL_UNRESOLVED"
}
```

**SDK Code**

```python App only, on an aggregator app
import requests

url = "https://epg-api.pixalate.com/v1/lookup"

payload = { "bundle": "com.pluto.tv" }
headers = {
    "x-api-key": "<apiKey>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript App only, on an aggregator app
const url = 'https://epg-api.pixalate.com/v1/lookup';
const options = {
  method: 'POST',
  headers: {'x-api-key': '<apiKey>', 'Content-Type': 'application/json'},
  body: '{"bundle":"com.pluto.tv"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go App only, on an aggregator app
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://epg-api.pixalate.com/v1/lookup"

	payload := strings.NewReader("{\n  \"bundle\": \"com.pluto.tv\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("x-api-key", "<apiKey>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby App only, on an aggregator app
require 'uri'
require 'net/http'

url = URI("https://epg-api.pixalate.com/v1/lookup")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"bundle\": \"com.pluto.tv\"\n}"

response = http.request(request)
puts response.read_body
```

```java App only, on an aggregator app
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://epg-api.pixalate.com/v1/lookup")
  .header("x-api-key", "<apiKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"bundle\": \"com.pluto.tv\"\n}")
  .asString();
```

```php App only, on an aggregator app
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://epg-api.pixalate.com/v1/lookup', [
  'body' => '{
  "bundle": "com.pluto.tv"
}',
  'headers' => [
    'Content-Type' => 'application/json',
    'x-api-key' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp App only, on an aggregator app
using RestSharp;

var client = new RestClient("https://epg-api.pixalate.com/v1/lookup");
var request = new RestRequest(Method.POST);
request.AddHeader("x-api-key", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"bundle\": \"com.pluto.tv\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift App only, on an aggregator app
import Foundation

let headers = [
  "x-api-key": "<apiKey>",
  "Content-Type": "application/json"
]
let parameters = ["bundle": "com.pluto.tv"] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://epg-api.pixalate.com/v1/lookup")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### Same app plus a channel identifier

**Request**

```json
{
  "bundle": "com.pluto.tv",
  "channelId": "espn",
  "os": "roku"
}
```

**Response**

```json
{
  "notices": [],
  "candidates": [],
  "reason": "CHANNEL_UNRESOLVED"
}
```

**SDK Code**

```python Same app plus a channel identifier
import requests

url = "https://epg-api.pixalate.com/v1/lookup"

payload = {
    "bundle": "com.pluto.tv",
    "channelId": "espn",
    "os": "roku"
}
headers = {
    "x-api-key": "<apiKey>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript Same app plus a channel identifier
const url = 'https://epg-api.pixalate.com/v1/lookup';
const options = {
  method: 'POST',
  headers: {'x-api-key': '<apiKey>', 'Content-Type': 'application/json'},
  body: '{"bundle":"com.pluto.tv","channelId":"espn","os":"roku"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Same app plus a channel identifier
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://epg-api.pixalate.com/v1/lookup"

	payload := strings.NewReader("{\n  \"bundle\": \"com.pluto.tv\",\n  \"channelId\": \"espn\",\n  \"os\": \"roku\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("x-api-key", "<apiKey>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Same app plus a channel identifier
require 'uri'
require 'net/http'

url = URI("https://epg-api.pixalate.com/v1/lookup")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"bundle\": \"com.pluto.tv\",\n  \"channelId\": \"espn\",\n  \"os\": \"roku\"\n}"

response = http.request(request)
puts response.read_body
```

```java Same app plus a channel identifier
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://epg-api.pixalate.com/v1/lookup")
  .header("x-api-key", "<apiKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"bundle\": \"com.pluto.tv\",\n  \"channelId\": \"espn\",\n  \"os\": \"roku\"\n}")
  .asString();
```

```php Same app plus a channel identifier
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://epg-api.pixalate.com/v1/lookup', [
  'body' => '{
  "bundle": "com.pluto.tv",
  "channelId": "espn",
  "os": "roku"
}',
  'headers' => [
    'Content-Type' => 'application/json',
    'x-api-key' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp Same app plus a channel identifier
using RestSharp;

var client = new RestClient("https://epg-api.pixalate.com/v1/lookup");
var request = new RestRequest(Method.POST);
request.AddHeader("x-api-key", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"bundle\": \"com.pluto.tv\",\n  \"channelId\": \"espn\",\n  \"os\": \"roku\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift Same app plus a channel identifier
import Foundation

let headers = [
  "x-api-key": "<apiKey>",
  "Content-Type": "application/json"
]
let parameters = [
  "bundle": "com.pluto.tv",
  "channelId": "espn",
  "os": "roku"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://epg-api.pixalate.com/v1/lookup")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```