Instapage API
Current state of the API:
What is the Instapage API?
The Instapage API allows developers to interact with the Instapage platform programmatically. By utilizing our API, you can create, update, retrieve, and delete various resources within Instapage, such as landing pages, leads, forms, and more. The API is designed to be RESTful, making it straightforward to integrate with any application or service.
Getting Started
To start using the Instapage API, you'll need to:
- Obtain API Credentials: Generate your API key from the Instapage dashboard.
- Understand API Endpoints: Familiarize yourself with the available endpoints and their functions.
- Make Your First Request: Learn how to authenticate and make basic API requests.
Throughout this documentation, you'll find detailed explanations, code examples, and best practices to help you effectively utilize the Instapage API.
Dictionary
Page
In Instapage, a page is a landing page made within the platform [Creating a new page][How do I publish my page to my own domain]. The page is a standalone webpage for a specific marketing campaign that aims to convert visitors into leads or customers[Lead management].
Workspace
In Instapage, a workspace is a designated area for managing landing pages for a company or project. Multiple workspaces can be created, and each workspace keeps its pages, integrations, domains, and other assets separate from other workspaces[Workspace settings and account settings].
Scheduling
Scheduling allows users to set specific dates and times to publish and unpublish landing pages. Scheduling is available for Custom Domain (CNAME) and Demo Page publishing and offers flexibility for campaign management[Scheduling].
Personalization
Personalization involves creating customized experiences for different audience segments on landing pages. This feature allows users to tailor content and design to resonate with specific visitor groups, enhancing engagement and conversion rates[Personalization creating personalized experiences for different audiences].
Leads
A lead is information submitted by a visitor through a form on a landing page. Lead generation is a crucial aspect of digital marketing, and landing pages are often a primary tool for capturing leads. If a form collects a visitor's name, email, city, and zip code, that constitutes a lead[Lead management].
Changelog
2025-03-26
Added
Pages
We introduced a new endpoint for managing pages within groups:
PATCH https://api.instapage.com/v1/workspaces/{workspaceId}/pages/{pageId}
- Update a page's properties, allowing users to move pages between groups or remove them from groups.
2025-03-25
Added
Form Submissions
We introduced a new Delete Form Submissions API to allow users to programmatically delete form submissions.
DELETE https://api.instapage.com/v1/workspaces/{workspaceId}/submissions
- Delete up to 100 form submissions in a single request. Deletion is irreversible, so use it with caution.
2025-03-19
Changed
Pages
GET https://api.instapage.com/v1/workspaces/{workspaceId}/pages
- Added new propertygroupId
to responseGET https://api.instapage.com/v1/workspaces/{workspaceId}/pages
- Added new filtering with query parameterwithGroupId
GET https://api.instapage.com/v1/workspaces/{workspaceId}/pages/{pageId}
- Added new propertygroupId
to response
2025-03-11
Added
Groups
We introduced a new Groups API to help users manage page groups (folders) within a workspace.
GET https://api.instapage.com/v1/workspaces/{workspaceId}/groups
- Retrieve all groups (folders) for a specific workspace.POST https://api.instapage.com/v1/workspaces/{workspaceId}/groups
- Create a new group (folder) in a workspace.PUT https://api.instapage.com/v1/workspaces/{workspaceId}/groups/{groupId}
- Update an existing group name (folder) in a workspace.DELETE https://api.instapage.com/v1/workspaces/{workspaceId}/groups/{groupId}
- Delete an empty group (folder) from a workspace.
2025-02-27
Added
Team Members
We enhanced the Team Members API to allow better workspace management by enabling bulk invitations, role updates, and removals.
POST https://api.instapage.com/v1/workspaces/{workspaceId}/team-members
- Invites multiple team members to join a workspace with specified accessLevels.PUT https://api.instapage.com/v1/workspaces/{workspaceId}/team-members
- Updates the roles of existing team members in a workspace. This endpoint allows bulk role updates for multiple team members simultaneously.DELETE https://api.instapage.com/v1/workspaces/{workspaceId}/team-members
- Remove one or more team members from a workspace. This endpoint supports bulk removal operations.
Authorization
To authorize, use this code:
# With shell, you can just pass the correct header with each request
curl "api_endpoint_here" \
-H "Authorization: Bearer API_KEY"
await fetch(url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${API_KEY}`,
},
});
await fetch(url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${API_KEY}`,
},
});
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . $apiKey
],
]);
Make sure to replace
API_KEY
with your API key.
Instapage expects for the API key to be included in all API requests to the server in a header.
Limitations
Token Scope
A personal token inherits all permissions from its creator. This means the token's access level may vary across workspaces, depending on the creator's role in each workspace. Additionally, workspace-specific API limits apply based on the owner's plan. API requests are subject to both the token’s permission level and the workspace owner's plan limitations.
- Tokens cannot exceed the permissions granted to their creator.
- If the creator's permissions change (e.g., role update, removal), the token’s access level is updated automatically.
- Expired or revoked tokens will return a
401 Unauthorized
response.
Rate Limit
The API allows up to 200 requests per minute. Exceeding this limit results in a 429 Too Many Requests
response.
- The rate limit is enforced per token and per IP address.
- High-frequency requests may be throttled before reaching the limit.
Handling Rate Limits
#!/bin/bash
URL="https://api.instapage.com/v1/workspaces"
TOKEN="your_api_token"
MAX_RETRIES=5
INITIAL_DELAY=1 # Initial delay in seconds
retry_count=0
delay=$INITIAL_DELAY
while [ $retry_count -lt $MAX_RETRIES ]; do
response=$(curl -s -o response.txt -w "%{http_code}" -H "Authorization: Bearer $TOKEN" "$URL")
if [ "$response" -ne 429 ]; then
cat response.txt # Output the response if it's not a rate limit error
exit 0
fi
echo "Rate limit exceeded. Retrying in $delay seconds..."
sleep $delay
# Exponential backoff (double the delay each time)
delay=$((delay * 2))
retry_count=$((retry_count + 1))
done
echo "Max retries reached. Exiting."
exit 1
class RateLimiter {
constructor(maxRequests, intervalMs) {
this.maxRequests = maxRequests;
this.intervalMs = intervalMs;
this.requestTimestamps = [];
}
async schedule() {
const now = Date.now();
this.requestTimestamps = this.requestTimestamps.filter(ts => ts > now - this.intervalMs);
if (this.requestTimestamps.length >= this.maxRequests) {
const waitTime = this.requestTimestamps[0] + this.intervalMs - now;
console.warn(`Rate limit approaching. Waiting ${waitTime}ms before next request.`);
await new Promise(resolve => setTimeout(resolve, waitTime));
}
this.requestTimestamps.push(Date.now());
}
}
function wrapFunctionWithRateLimit(fn, rateLimiter) {
return async function (...args) {
await rateLimiter.schedule();
return fn(...args);
};
}
function wrapFunctionWithRetries(fn, retries = 5, initialDelay = 1000) {
return async function (...args) {
let attempt = 0;
let delay = initialDelay;
while (attempt <= retries) {
const response = await fn(...args); // Call the wrapped function
if (response.status === 429) {
const backoff = delay * (2 ** attempt); // Exponential backoff
console.warn(`Rate limit exceeded. Retrying in ${backoff / 1000} seconds...`);
await new Promise(resolve => setTimeout(resolve, backoff));
attempt++;
} else {
return response; // Return the full response if it's not a 429
}
}
throw new Error("Max retries reached. Request failed.");
};
}
async function fetchWorkspaces(page, apiKey) {
const url = `https://api.instapage.com/v1/workspaces?page=${page}`;
const response = await fetch(url, {
headers: { 'Authorization': `Bearer ${apiKey}` },
});
if (!response.ok) {
throw new Error(`Failed to fetch workspaces: ${response.statusText}`);
}
return response; // Return the whole response object
}
// Usage example
// Create rate limiter instance (200 requests per minute)
const rateLimiter = new RateLimiter(200, 60000);
// Wrap the function with both rate limiting and retry logic
const fetchWorkspacesGracefully = wrapFunctionWithRateLimit(
wrapFunctionWithRetries(fetchWorkspaces, 5, 1000),
rateLimiter
);
fetchWorkspacesGracefully(1, 'API_KEY');
type Workspace = {
workspaceId: number;
ownerId: number;
workspaceName: string;
accessLevel: 'owner' | 'editor' | 'manager' | 'viewer';
createdAt: number;
};
type Meta = {
page: number;
};
type WorkspacesResponse = {
data: Workspace[];
meta: Meta;
};
class RateLimiter {
private maxRequests: number;
private intervalMs: number;
private requestTimestamps: number[];
constructor(maxRequests: number, intervalMs: number) {
this.maxRequests = maxRequests;
this.intervalMs = intervalMs;
this.requestTimestamps = [];
}
async schedule(): Promise<void> {
const now = Date.now();
this.requestTimestamps = this.requestTimestamps.filter(ts => ts > now - this.intervalMs);
if (this.requestTimestamps.length >= this.maxRequests) {
const waitTime = this.requestTimestamps[0] + this.intervalMs - now;
console.warn(`Rate limit approaching. Waiting ${waitTime}ms before next request.`);
await new Promise(resolve => setTimeout(resolve, waitTime));
}
this.requestTimestamps.push(Date.now());
}
}
function wrapFunctionWithRateLimit<T extends Function>(fn: T, rateLimiter: RateLimiter): T {
return async function (...args: any[]) {
await rateLimiter.schedule();
return fn(...args);
} as T;
}
function wrapFunctionWithRetries<T extends Function>(
fn: T,
retries = 5,
initialDelay = 1000
): T {
return async function (...args: any[]) {
let attempt = 0;
let delay = initialDelay;
while (attempt <= retries) {
const response = await fn(...args);
if (response.status === 429) {
const backoff = delay * (2 ** attempt);
console.warn(`Rate limit exceeded. Retrying in ${backoff / 1000} seconds...`);
await new Promise(resolve => setTimeout(resolve, backoff));
attempt++;
} else {
return response; // Return the full response if it's not a 429
}
}
throw new Error("Max retries reached. Request failed.");
} as T;
}
async function fetchWorkspaces(page: number, apiKey: string): Promise<WorkspacesResponse> {
const url = `https://api.instapage.com/v1/workspaces?page=${page}`;
const response = await fetch(url, {
headers: { 'Authorization': `Bearer ${apiKey}` },
});
if (!response.ok) {
throw new Error(`Failed to fetch workspaces: ${response.statusText}`);
}
const data: WorkspacesResponse = await response.json();
return data;
}
// Usage example
// Create a rate limiter instance (200 requests per minute)
const rateLimiter = new RateLimiter(200, 60000);
// Wrap the function with both rate limiting and retry logic
const fetchWorkspacesGracefully = wrapFunctionWithRateLimit(
wrapFunctionWithRetries(fetchWorkspaces, 5, 1000),
rateLimiter
);
fetchWorkspacesGracefully(1, 'API_KEY')
.then(data => {
console.log('Fetched workspaces:', data);
})
.catch(error => {
console.error('Error fetching workspaces:', error);
});
<?php
declare(strict_types=1);
namespace InstapageWorkspacesFetcher;
enum AccessLevel: string
{
case Owner = 'owner';
case Editor = 'editor';
case Manager = 'manager';
case Viewer = 'viewer';
}
readonly class Workspace
{
public function __construct(
public int $workspaceId,
public int $ownerId,
public string $workspaceName,
public AccessLevel $accessLevel,
public int $createdAt
) {}
public function toArray(): array
{
return [
'workspaceId' => $this->workspaceId,
'ownerId' => $this->ownerId,
'workspaceName' => $this->workspaceName,
'accessLevel' => $this->accessLevel->value,
'createdAt' => $this->createdAt
];
}
public static function fromArray(array $data): self
{
return new self(
$data['workspaceId'],
$data['ownerId'],
$data['workspaceName'],
AccessLevel::from($data['accessLevel']),
$data['createdAt']
);
}
}
readonly class Meta
{
public function __construct(
public int $page
) {}
public function toArray(): array
{
return [
'page' => $this->page
];
}
public static function fromArray(array $data): self
{
return new self($data['page']);
}
}
class WorkspacesResponse
{
/**
* @param Workspace[] $data
*/
public function __construct(
private array $data,
private Meta $meta
) {}
/**
* @return Workspace[]
*/
public function getData(): array
{
return $this->data;
}
public function getMeta(): Meta
{
return $this->meta;
}
}
class RateLimiter
{
private int $maxRequests;
private int $intervalMs;
private array $requestTimestamps;
public function __construct(int $maxRequests, int $intervalMs)
{
$this->maxRequests = $maxRequests;
$this->intervalMs = $intervalMs;
$this->requestTimestamps = [];
}
public function schedule(): void
{
$now = (int) (microtime(true) * 1000);
$this->requestTimestamps = array_filter(
$this->requestTimestamps,
fn($timestamp) => $timestamp > $now - $this->intervalMs
);
if (count($this->requestTimestamps) >= $this->maxRequests) {
$waitTime = $this->requestTimestamps[0] + $this->intervalMs - $now;
echo "Rate limit approaching. Waiting {$waitTime}ms before next request.\n";
usleep($waitTime * 1000); // Sleep in microseconds
}
$this->requestTimestamps[] = (int) (microtime(true) * 1000);
}
}
class WorkspacesFetcher
{
public function __construct(
private string $baseUrl,
private string $apiKey,
private RateLimiter $rateLimiter // Pass rateLimiter as parameter
) {}
public function fetchWorkspaces(int $page, int $retries = 5, int $initialDelay = 1000): WorkspacesResponse
{
$url = sprintf(
'%s/v1/workspaces?page=%d',
$this->baseUrl,
$page
);
$attempt = 0;
$delay = $initialDelay;
while ($attempt <= $retries) {
$this->rateLimiter->schedule(); // Ensure rate limiting is respected
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
]
]);
$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new \RuntimeException('CURL Error: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode === 200) {
$responseData = json_decode($responseBody, true);
return new WorkspacesResponse(
array_map(
fn($workspaceData) => Workspace::fromArray($workspaceData),
$responseData['data']
),
Meta::fromArray($responseData['meta'])
);
}
if ($httpCode === 429) {
$backoff = $delay * (2 ** $attempt); // Exponential backoff
echo "Rate limit exceeded. Retrying in {$backoff / 1000} seconds...\n";
usleep($backoff * 1000); // Sleep in microseconds
$attempt++;
} else {
throw new \RuntimeException("Failed to fetch workspaces: HTTP $httpCode");
}
}
throw new \RuntimeException("Max retries reached. Request failed.");
}
}
// Example usage
try {
$baseUrl = 'https://api.instapage.com';
$apiKey = 'your_api_key_here';
$page = 1;
$rateLimiter = new RateLimiter(200, 60000); // Create a rate limiter instance
$fetcher = new WorkspacesFetcher($baseUrl, $apiKey, $rateLimiter); // Pass it to the fetcher
$workspacesResponse = $fetcher->fetchWorkspaces($page);
// Process workspaces
foreach ($workspacesResponse->getData() as $workspace) {
print_r($workspace->toArray());
}
// Access metadata
$meta = $workspacesResponse->getMeta();
print_r($meta->toArray());
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
To ensure smooth API usage, we recommend implementing strategies to handle rate limit scenarios proactively. Applications should anticipate and explicitly manage rate limits in their code.
The best approach is to throttle requests on the client side and implement retries with an exponential backoff strategy.
This means gradually increasing the delay between retries based on the number of 429 Too Many Requests
responses received within the last minute.
We recommend leveraging well-established third-party libraries for handling rate limits and retries, as they are tested, proven, and actively maintained. Utilizing these libraries can save time and ensure reliable implementation, so you do not need to implement these features from scratch.
For JavaScript (Node.js), you can consider using libraries like:
- axios-rate-limit – This library integrates with Axios to automatically throttle requests and handle retries with backoff.
- p-throttle – Provides a simple way to throttle promises, supporting both rate limits and retries.
For PHP, you can use libraries like:
- Guzzle with middleware like Guzzle Retry Middleware – You can use this combination to add rate limiting and retry logic with backoff.
- Stiphle – Stiphle is a little library to try and provide an easy way of throttling/rate limit requests.
These libraries can help you simplify the implementation of rate limits and retries, allowing you to focus more on the core functionality of your application.
Workspaces
Get All Workspaces
Retrieve all workspaces that the provided token has access to.
HTTP Request
https://api.instapage.com/v1/workspaces
curl "https://api.instapage.com/v1/workspaces?page=1" \
-H "Authorization: Bearer API_KEY"
async function fetchWorkspaces(page, apiKey) {
const response = await fetch(`https://api.instapage.com/v1/workspaces?page=${page}`, {
headers: {
'Authorization': `Bearer ${apiKey}`,
},
});
if (!response.ok) {
throw new Error(`Failed to fetch workspaces: ${response.statusText}`);
}
const data = await response.json();
return data;
}
type Workspace = {
workspaceId: number;
ownerId: number;
workspaceName: string;
accessLevel: 'owner' | 'editor' | 'manager' | 'viewer';
createdAt: number;
};
type Meta = {
page: number;
};
type WorkspacesResponse = {
data: Workspace[];
meta: Meta;
};
async function fetchWorkspaces(page: number, apiKey: string): Promise<WorkspacesResponse> {
const response = await fetch(`https://api.instapage.com/v1/workspaces?page=${page}`, {
headers: {
'Authorization': `Bearer ${apiKey}`,
},
});
if (!response.ok) {
throw new Error(`Failed to fetch workspaces: ${response.statusText}`);
}
const data: WorkspacesResponse = await response.json();
return data;
}
<?php
declare(strict_types=1);
namespace InstapageWorkspacesFetcher;
enum AccessLevel: string
{
case Owner = 'owner';
case Editor = 'editor';
case Manager = 'manager';
case Viewer = 'viewer';
}
readonly class Workspace
{
public function __construct(
public int $workspaceId,
public int $ownerId,
public string $workspaceName,
public AccessLevel $accessLevel,
public int $createdAt
) {}
public function toArray(): array
{
return [
'workspaceId' => $this->workspaceId,
'ownerId' => $this->ownerId,
'workspaceName' => $this->workspaceName,
'accessLevel' => $this->accessLevel->value,
'createdAt' => $this->createdAt
];
}
public static function fromArray(array $data): self
{
return new self(
$data['workspaceId'],
$data['ownerId'],
$data['workspaceName'],
AccessLevel::from($data['accessLevel']),
$data['createdAt']
);
}
}
readonly class Meta
{
public function __construct(
public int $page
) {}
public function toArray(): array
{
return [
'page' => $this->page
];
}
public static function fromArray(array $data): self
{
return new self($data['page']);
}
}
class WorkspacesResponse
{
/**
* @param Workspace[] $data
*/
public function __construct(
private array $data,
private Meta $meta
) {}
/**
* @return Workspace[]
*/
public function getData(): array
{
return $this->data;
}
public function getMeta(): Meta
{
return $this->meta;
}
}
class WorkspacesFetcher
{
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
public function fetchWorkspaces(int $page): WorkspacesResponse
{
$url = sprintf(
'%s/v1/workspaces?page=%d',
$this->baseUrl,
$page
);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
]
]);
$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new \RuntimeException('CURL Error: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode !== 200) {
throw new \RuntimeException("Failed to fetch workspaces: HTTP $httpCode");
}
$responseData = json_decode($responseBody, true);
return new WorkspacesResponse(
array_map(
fn($workspaceData) => Workspace::fromArray($workspaceData),
$responseData['data']
),
Meta::fromArray($responseData['meta'])
);
}
}
// Example usage
try {
$baseUrl = 'https://api.instapage.com';
$apiKey = 'your_api_key_here';
$page = 1;
$fetcher = new WorkspacesFetcher($baseUrl, $apiKey);
$workspacesResponse = $fetcher->fetchWorkspaces($page);
// Process workspaces
foreach ($workspacesResponse->getData() as $workspace) {
// Do something with each workspace
print_r($workspace->toArray());
}
// Access metadata
$meta = $workspacesResponse->getMeta();
print_r($meta->toArray());
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
The above request returns JSON structured like this:
{
"data": [
{
"workspaceId": 1177,
"ownerId": 1319,
"workspaceName": "Personal Projects",
"accessLevel": "owner",
"createdAt": 1262304000
},
{
"workspaceId": 1801,
"ownerId": 1637,
"workspaceName": "Personal Projects",
"accessLevel": "viewer",
"createdAt": 1262304000
},
{
"workspaceId": 2637,
"ownerId": 2517,
"workspaceName": "Personal Projects",
"accessLevel": "manager",
"createdAt": 1262304000
},
{
"workspaceId": 1771,
"ownerId": 1624,
"workspaceName": "Personal Projects",
"accessLevel": "editor",
"createdAt": 1262304000
}
],
"meta": {
"pagination": {
"currentPage": 1,
"perPage": 100,
"totalItemsCount": 4,
"totalPagesCount": 1,
"nextPage": null,
"previousPage": null
}
}
}
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Query Parameters
Parameter | Type | Default | Description |
---|---|---|---|
page |
number |
1 |
Specifies which page to fetch. Used for pagination purposes. |
name |
string |
null |
Optional name of the workspace to limit the result. Convenient if you already know how your workspace is named. (Case insensitive) |
Response JSON structure
JSON Path | Type | Description |
---|---|---|
data[].workspaceId |
number |
Unique identifier of the workspace. |
data[].ownerId |
number |
Identifier of the workspace owner. |
data[].workspaceName |
string |
Name of the workspace. |
data[].accessLevel |
string |
User's access level to the workspace. |
data[].createdAt |
number |
Creation date of the workspace in UNIX timestamp format. |
meta.pagination.currentPage |
number |
Number of the current page of results. |
meta.pagination.perPage |
number |
Number of items per page. |
meta.pagination.totalItemsCount |
number |
Total number of items. |
meta.pagination.totalPagesCount |
number |
Total number of pages. |
meta.pagination.nextPage |
number? |
Link to the next page (null if none). |
meta.pagination.previousPage |
number? |
Link to the previous page (null if none). |
Response status codes
Status | Description |
---|---|
200 |
The request was processed successfully. |
400 |
Bad request. Validation error — review input parameters. |
401 |
Unauthorized. Authentication failed or missing credentials. |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. Please slow down and retry after some time. |
500 |
Internal Server Error. An unexpected condition prevented the request from being fulfilled. |
Get Single Workspace
Retrieves details of a single workspace by its ID..
HTTP Request
https://api.instapage.com/v1/workspaces/{workspaceId}
curl -X GET "https://api.instapage.com/v1/workspaces/{workspaceId}" \
-H "Authorization: Bearer API_KEY" \
-H "Content-Type: application/json"
async function getWorkspace(workspaceId, apiKey) {
const response = await fetch(`https://api.instapage.com/v1/workspaces/${workspaceId}`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`Failed to get workspace: ${response.statusText}`);
}
const data = await response.json();
return data;
}
// Example usage
const apiKey = 'your_api_key_here';
const workspaceId = 12345;
getWorkspace(workspaceId. apiKey)
.then(workspace => {
console.log('Workspace:', workspace);
})
.catch(error => {
console.error('Error getting workspace:', error);
});
type AccessLevel = 'owner' | 'editor' | 'manager' | 'viewer';
interface Workspace {
workspaceId: number;
ownerId: number;
workspaceName: string;
accessLevel: AccessLevel;
createdAt: number;
}
class WorkspaceFetcher {
private baseUrl: string;
private apiKey: string;
constructor(baseUrl: string, apiKey: string) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
}
async getWorkspace(workspaceId: number): Promise<Workspace> {
const url = `${this.baseUrl}/v1/workspaces/${workspaceId}`;
const response = await fetch(url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`Failed to get workspace: ${response.statusText}`);
}
const responseData = await response.json();
return responseData.data;
}
}
// Example usage
const baseUrl = 'https://api.instapage.com';
const apiKey = 'your_api_key_here';
const fetcher = new WorkspaceFetcher(baseUrl, apiKey);
const workspaceId = 12345;
fetcher.getWorkspace(workspaceId)
.then(workspace => {
console.log('Workspace:', workspace);
})
.catch(error => {
console.error('Error getting workspace:', error);
});
<?php
declare(strict_types=1);
namespace InstapageWorkspacesFetcher;
enum AccessLevel: string
{
case Owner = 'owner';
case Editor = 'editor';
case Manager = 'manager';
case Viewer = 'viewer';
}
readonly class Workspace
{
public function __construct(
public int $workspaceId,
public int $ownerId,
public string $workspaceName,
public AccessLevel $accessLevel,
public int $createdAt
) {}
public static function fromArray(array $data): self
{
return new self(
$data['workspaceId'],
$data['ownerId'],
$data['workspaceName'],
AccessLevel::from($data['accessLevel']),
$data['createdAt']
);
}
}
class WorkspacesFetcher
{
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
public function getWorkspace(int $workspaceId): Workspace
{
$url = sprintf('%s/v1/workspaces/%d', $this->baseUrl, $workspaceId);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
]
]);
$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new \RuntimeException('CURL Error: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode !== 200) {
throw new \RuntimeException("Failed to fetch workspace: HTTP $httpCode");
}
$responseData = json_decode($responseBody, true);
return Workspace::fromArray($responseData['data']);
}
}
// Example usage
try {
$baseUrl = 'https://api.instapage.com';
$apiKey = 'your_api_key_here';
$workspaceId = 12345;
$fetcher = new WorkspacesFetcher($baseUrl, $apiKey);
$workspace = $fetcher->getWorkspace($workspaceId);
echo 'Workspace retrieved: ' . json_encode($workspace, JSON_PRETTY_PRINT);
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
The above request returns JSON structured like this:
{
"data": {
"workspaceId": 2000,
"ownerId": 1319,
"workspaceName": "New Workspace Name",
"accessLevel": "owner",
"createdAt": 1700304000
}
}
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
The ID of the workspace to retrieve pages for |
Response JSON structure
JSON Path | Type | Description |
---|---|---|
data.workspaceId |
number |
Unique identifier of the workspace. |
data.ownerId |
number |
Identifier of the workspace owner. |
data.workspaceName |
string |
Name of the workspace. |
data.accessLevel |
string |
User's access level to the workspace. |
data.createdAt |
number |
Creation date of the workspace in UNIX timestamp format. |
Response status codes
Status | Description |
---|---|
200 |
Request was processed successfully. |
400 |
Bad request. |
401 |
Unauthorized. Authentication failed or missing credentials. |
404 |
Workspace not found. |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. |
500 |
Internal Server Error. An unexpected condition prevented the request from being fulfilled. |
Add New Workspace
Create a new workspace for the authenticated user.
Note: Each workspace must have a unique name under the same owner.
HTTP Request
https://api.instapage.com/v1/workspaces
curl -X POST "https://api.instapage.com/v1/workspaces" \
-H "Authorization: Bearer API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "New Workspace"}'
async function createWorkspace(name, apiKey) {
const response = await fetch(`https://api.instapage.com/v1/workspaces`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ name }),
});
if (!response.ok) {
throw new Error(`Failed to create workspace: ${response.statusText}`);
}
const data = await response.json();
return data;
}
type AccessLevel = 'owner' | 'editor' | 'manager' | 'viewer';
interface Workspace {
workspaceId: number;
ownerId: number;
workspaceName: string;
accessLevel: AccessLevel;
createdAt: number;
}
class WorkspaceCreator {
private baseUrl: string;
private apiKey: string;
constructor(baseUrl: string, apiKey: string) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
}
async createWorkspace(name: string): Promise<Workspace> {
const url = `${this.baseUrl}/v1/workspaces`;
const payload = JSON.stringify({ name });
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
'Content-Length': payload.length.toString(),
},
body: payload,
});
if (!response.ok) {
throw new Error(`Failed to create workspace: ${response.statusText}`);
}
const responseData = await response.json();
return responseData.data;
}
}
// Example usage
const baseUrl = 'https://api.instapage.com';
const apiKey = 'your_api_key_here';
const creator = new WorkspaceCreator(baseUrl, apiKey);
creator.createWorkspace('New Workspace Name')
.then(workspace => {
console.log('Created Workspace:', workspace);
})
.catch(error => {
console.error('Error creating workspace:', error);
});
<?php
declare(strict_types=1);
namespace InstapageWorkspacesCreator;
enum AccessLevel: string
{
case Owner = 'owner';
case Editor = 'editor';
case Manager = 'manager';
case Viewer = 'viewer';
}
readonly class Workspace
{
public function __construct(
public int $workspaceId,
public int $ownerId,
public string $workspaceName,
public AccessLevel $accessLevel,
public int $createdAt
) {}
public static function fromArray(array $data): self
{
return new self(
$data['workspaceId'],
$data['ownerId'],
$data['workspaceName'],
AccessLevel::from($data['accessLevel']),
$data['createdAt']
);
}
}
class WorkspacesCreator
{
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
public function createWorkspace(string $name): WorkspacesResponse
{
$url = sprintf(
'%s/v1/workspaces',
$this->baseUrl,
$page
);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
]
]);
$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new \RuntimeException('CURL Error: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode !== 200) {
throw new \RuntimeException("Failed to fetch workspaces: HTTP $httpCode");
}
$responseData = json_decode($responseBody, true);
return Workspace::fromArray($responseData['data']);
}
}
// Example usage
try {
$baseUrl = 'https://api.instapage.com';
$apiKey = 'your_api_key_here';
$newWorkspaceName = 'workspace-12';
$creator = new WorkspacesCreator($baseUrl, $apiKey);
$createResponse = $creator->createWorkspace($newWorkspaceName);
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
The above request returns JSON structured like this:
{
"data": {
"workspaceId": 2000,
"ownerId": 1319,
"workspaceName": "New Workspace Name",
"accessLevel": "owner",
"createdAt": 1700304000
}
}
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Request Body
Parameter | Type | Required | Description |
---|---|---|---|
name |
string |
Yes | The unique name of the workspace. |
Response JSON structure
JSON Path | Type | Description |
---|---|---|
data.workspaceId |
number |
Unique identifier of the workspace. |
data.ownerId |
number |
Identifier of the workspace owner. |
data.workspaceName |
string |
Name of the workspace. |
data.accessLevel |
string |
User's access level to the workspace. |
data.createdAt |
number |
Creation date of the workspace in UNIX timestamp format. |
Response status codes
Status | Description |
---|---|
201 |
The workspace was created successfully. |
400 |
Bad request. Validation error — review input parameters. |
401 |
Unauthorized. Authentication failed or missing credentials. |
409 |
Conflict. A workspace with the same name already exists. |
422 |
Unprocessable Entity. Limit of workspaces has been reached. |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. |
500 |
Internal Server Error. An unexpected condition prevented the request from being fulfilled. |
Rename Workspace
Updates the name of an existing workspace.
HTTP Request
https://api.instapage.com/v1/workspaces/{workspaceId}
curl -X PATCH "https://api.instapage.com/v1/workspaces/{workspaceId}" \
-H "Authorization: Bearer API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "New Workspace Name"}'
async function renameWorkspace(workspaceId, name, apiKey) {
const response = await fetch(`https://api.instapage.com/v1/workspaces/${workspaceId}`, {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ name }),
});
if (!response.ok) {
throw new Error(`Failed to rename workspace: ${response.statusText}`);
}
const data = await response.json();
return data;
}
// Example usage
const workspaceId = 123;
const newWorkspaceName = 'Renamed Workspace';
const apiKey = 'your_api_key_here';
renameWorkspace(workspaceId, newWorkspaceName, apiKey)
.then(data => {
console.log('Renamed Workspace:', data);
})
.catch(error => {
console.error('Error renaming workspace:', error);
});
type AccessLevel = 'owner' | 'editor' | 'manager' | 'viewer';
interface Workspace {
workspaceId: number;
ownerId: number;
workspaceName: string;
accessLevel: AccessLevel;
createdAt: number;
}
class WorkspaceRenamer {
private baseUrl: string;
private apiKey: string;
constructor(baseUrl: string, apiKey: string) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
}
async renameWorkspace(workspaceId: number, newName: string): Promise<Workspace> {
const url = `${this.baseUrl}/v1/workspaces/${workspaceId}`;
const payload = JSON.stringify({ name: newName });
const response = await fetch(url, {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
'Content-Length': payload.length.toString(),
},
body: payload,
});
if (!response.ok) {
throw new Error(`Failed to rename workspace: ${response.statusText}`);
}
const responseData = await response.json();
return responseData.data;
}
}
// Example usage
const baseUrl = 'https://api.instapage.com';
const apiKey = 'your_api_key_here';
const renamer = new WorkspaceRenamer(baseUrl, apiKey);
const workspaceId = 123;
const newWorkspaceName = 'Renamed Workspace';
renamer.renameWorkspace(workspaceId, newWorkspaceName)
.then(workspace => {
console.log('Renamed Workspace:', workspace);
})
.catch(error => {
console.error('Error renaming workspace:', error);
});
<?php
declare(strict_types=1);
namespace InstapageWorkspacesRenamer;
enum AccessLevel: string
{
case Owner = 'owner';
case Editor = 'editor';
case Manager = 'manager';
case Viewer = 'viewer';
}
readonly class Workspace
{
public function __construct(
public int $workspaceId,
public int $ownerId,
public string $workspaceName,
public AccessLevel $accessLevel,
public int $createdAt
) {}
public static function fromArray(array $data): self
{
return new self(
$data['workspaceId'],
$data['ownerId'],
$data['workspaceName'],
AccessLevel::from($data['accessLevel']),
$data['createdAt']
);
}
}
class WorkspacesRenamer
{
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
public function renameWorkspace(int $workspaceId, string $newName): Workspace
{
$url = sprintf('%s/v1/workspaces/%d', $this->baseUrl, $workspaceId);
$payload = json_encode(['name' => $newName]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json',
],
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_POSTFIELDS => $payload,
]);
$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new \RuntimeException('CURL Error: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode !== 200) {
throw new \RuntimeException("Failed to rename workspace: HTTP $httpCode");
}
$responseData = json_decode($responseBody, true);
return Workspace::fromArray($responseData['data']);
}
}
// Example usage
try {
$baseUrl = 'https://api.instapage.com';
$apiKey = 'your_api_key_here';
$workspaceId = 123;
$newWorkspaceName = 'Renamed Workspace';
$renamer = new WorkspacesRenamer($baseUrl, $apiKey);
$updatedWorkspace = $renamer->renameWorkspace($workspaceId, $newWorkspaceName);
echo 'Workspace renamed to: ' . $updatedWorkspace->workspaceName;
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
The above request returns JSON structured like this:
{
"data": {
"workspaceId": 2000,
"ownerId": 1319,
"workspaceName": "New Workspace Name",
"accessLevel": "owner",
"createdAt": 1700304000
}
}
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
The ID of the workspace to update. |
Request Body
Field | Type | Description |
---|---|---|
name |
string |
The new name for the workspace. |
Response JSON structure
JSON Path | Type | Description |
---|---|---|
data.workspaceId |
number |
Unique identifier of the workspace. |
data.ownerId |
number |
Identifier of the workspace owner. |
data.workspaceName |
string |
Updated name of the workspace. |
data.accessLevel |
string |
User's access level to the workspace. |
data.createdAt |
number |
Creation date of the workspace in UNIX timestamp format. |
Response status codes
Status | Description |
---|---|
200 |
Request was processed successfully. |
400 |
Bad request. Invalid input provided. |
401 |
Unauthorized. Authentication failed or missing credentials. |
404 |
Workspace not found. |
409 |
Conflict. The workspace name is already taken. |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. |
500 |
Internal Server Error. An unexpected condition prevented the request from being fulfilled. |
Delete Workspace
Deletes a workspace by its ID.
HTTP Request
https://api.instapage.com/v1/workspaces/{workspaceId}
curl -X DELETE "https://api.instapage.com/v1/workspaces/{workspaceId}" \
-H "Authorization: Bearer API_KEY" \
-H "Content-Type: application/json"
async function deleteWorkspace(workspaceId, apiKey) {
const response = await fetch(`https://api.instapage.com/v1/workspaces/${workspaceId}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`Failed to delete workspace: ${response.statusText}`);
}
return response.json();
}
// Example usage
const workspaceId = 'workspaceId_here';
const apiKey = 'your_api_key_here';
deleteWorkspace(workspaceId, apiKey)
.then(data => {
console.log('Workspace deleted:', data);
})
.catch(error => {
console.error('Error deleting workspace:', error);
});
class WorkspaceDeleter {
private baseUrl: string;
private apiKey: string;
constructor(baseUrl: string, apiKey: string) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
}
async deleteWorkspace(workspaceId: string): Promise<void> {
const url = `${this.baseUrl}/v1/workspaces/${workspaceId}`;
const response = await fetch(url, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`Failed to delete workspace: ${response.statusText}`);
}
console.log('Workspace deleted successfully');
}
}
// Example usage
const baseUrl = 'https://api.instapage.com';
const apiKey = 'your_api_key_here';
const deleter = new WorkspaceDeleter(baseUrl, apiKey);
deleter.deleteWorkspace('workspaceId_here')
.then(() => {
console.log('Workspace deleted');
})
.catch(error => {
console.error('Error deleting workspace:', error);
});
<?php
declare(strict_types=1);
namespace Instapage\PublicApi\UserInterface\Workspaces;
class WorkspaceDeleter
{
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
public function deleteWorkspace(string $workspaceId): void
{
$url = sprintf(
'%s/v1/workspaces/%s',
$this->baseUrl,
$workspaceId
);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
],
CURLOPT_CUSTOMREQUEST => 'DELETE'
]);
$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new \RuntimeException('CURL Error: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode !== 200) {
throw new \RuntimeException("Failed to delete workspace: HTTP $httpCode");
}
}
}
// Example usage
try {
$baseUrl = 'https://api.instapage.com';
$apiKey = 'your_api_key_here';
$workspaceId = 'workspaceId_here';
$deleter = new WorkspaceDeleter($baseUrl, $apiKey);
$deleter->deleteWorkspace($workspaceId);
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
The ID of the workspace to delete. |
Response status codes
Status | Description |
---|---|
200 |
Workspace successfully deleted. |
400 |
Bad request. |
401 |
Unauthorized. Authentication failed or missing credentials. |
404 |
Workspace not found. |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. |
500 |
Internal Server Error. An unexpected condition prevented the request from being fulfilled. |
Related articles
Team Members
Get All Team Members
Retrieve all team members for a specific workspace.
HTTP Request
https://api.instapage.com/v1/workspaces/{workspaceId}/team-members
curl "https://api.instapage.com/v1/workspaces/{workspaceId}/team-members" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json"
Example of body response from request
{
"data": [
{
"userId": 4379,
"email": "example_user@airslate.com",
"invitedAt": 1685608225,
"fullName": "John Smith",
"accessLevel": "editor",
"invitationStatus": "accepted",
"lastLoginAt": 1685608226,
"lastActivityInWorkspaceAt": null
},
{
"userId": 4377,
"email": "example1_user@airslate.com",
"invitedAt": 1684489737,
"fullName": "John Smith",
"accessLevel": "manager",
"invitationStatus": "accepted",
"lastLoginAt": 1685970113,
"lastActivityInWorkspaceAt": null
},
{
"userId": 4373,
"email": "example1_user@airslate.com",
"invitedAt": null,
"fullName": "John Smith",
"accessLevel": "owner",
"invitationStatus": "accepted",
"lastLoginAt": 1729173925,
"lastActivityInWorkspaceAt": 1729520831
}
],
"meta": []
}
async function fetchTeamMembers(workspaceId, apiKey) {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/team-members`;
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
});
if (!response.ok) {
throw new Error(`Failed to fetch team members: ${response.statusText}`);
}
const data = await response.json();
return data;
}
type TeamMember = {
userId: number;
email: string;
invitedAt: number | null;
fullName: string;
accessLevel: string;
invitationStatus: 'accepted' | 'pending';
lastLoginAt: number | null;
lastActivityInWorkspaceAt: number | null;
};
type TeamMembersResponse = {
data: TeamMember[];
meta: any;
};
async function fetchTeamMembers(
workspaceId: number,
apiKey: string
): Promise<TeamMembersResponse> {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/team-members`;
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
});
if (!response.ok) {
throw new Error(`Failed to fetch team members: ${response.statusText}`);
}
const data: TeamMembersResponse = await response.json();
return data;
}
<?php
declare(strict_types=1);
namespace InstapageTeamMembersFetcher;
enum InvitationStatus: string
{
case Accepted = 'accepted';
case Pending = 'pending';
}
readonly class TeamMember
{
public function __construct(
public int $userId,
public string $email,
public ?int $invitedAt,
public string $fullName,
public string $accessLevel,
public InvitationStatus $invitationStatus,
public ?int $lastLoginAt,
public ?int $lastActivityInWorkspaceAt
) {}
public function toArray(): array
{
return [
'userId' => $this->userId,
'email' => $this->email,
'invitedAt' => $this->invitedAt,
'fullName' => $this->fullName,
'accessLevel' => $this->accessLevel,
'invitationStatus' => $this->invitationStatus->value,
'lastLoginAt' => $this->lastLoginAt,
'lastActivityInWorkspaceAt' => $this->lastActivityInWorkspaceAt
];
}
public static function fromArray(array $data): self
{
return new self(
$data['userId'],
$data['email'],
$data['invitedAt'],
$data['fullName'],
$data['accessLevel'],
InvitationStatus::from($data['invitationStatus']),
$data['lastLoginAt'],
$data['lastActivityInWorkspaceAt']
);
}
}
class TeamMembersResponse
{
/**
* @param TeamMember[] $data
*/
public function __construct(
private array $data,
private array $meta
) {}
/**
* @return TeamMember[]
*/
public function getData(): array
{
return $this->data;
}
public function getMeta(): array
{
return $this->meta;
}
}
class TeamMembersFetcher
{
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
public function fetchTeamMembers(int $workspaceId): TeamMembersResponse
{
$url = sprintf(
'%s/v1/workspaces/%d/team-members',
$this->baseUrl,
$workspaceId
);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
]
]);
$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new \RuntimeException('CURL Error: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode !== 200) {
throw new \RuntimeException("Failed to fetch team members: HTTP $httpCode");
}
$responseData = json_decode($responseBody, true);
return new TeamMembersResponse(
array_map(
fn($memberData) => TeamMember::fromArray($memberData),
$responseData['data']
),
$responseData['meta']
);
}
}
// Example usage
try {
$baseUrl = 'https://api.instapage.com';
$apiKey = 'your_api_key_here';
$workspaceId = 1234;
$fetcher = new TeamMembersFetcher($baseUrl, $apiKey);
$teamMembersResponse = $fetcher->fetchTeamMembers($workspaceId);
// Process team members
foreach ($teamMembersResponse->getData() as $teamMember) {
// Do something with each team member
print_r($teamMember->toArray());
}
// Access metadata if needed
print_r($teamMembersResponse->getMeta());
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
The ID of the workspace to retrieve team members for |
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Response JSON structure
JSON Path | Type | Description |
---|---|---|
data[].userId |
number |
Unique identifier of the team member. |
data[].email |
string |
Email address of the team member. |
data[].invitedAt |
number? |
Timestamp when the team member was invited (null if owner). |
data[].fullName |
string |
Full name of the team member. |
data[].accessLevel |
string |
accessLevel of the team member (e.g., editor , manager , owner ). |
data[].invitationStatus |
string |
Status of the invitation (accepted or pending ). |
data[].lastLoginAt |
number? |
Timestamp of the team member's last login. |
data[].lastActivityInWorkspaceAt |
number? |
Timestamp of the team member's last activity in the workspace. |
Response status codes
Status | Description |
---|---|
200 |
The request was processed successfully. |
400 |
Bad request. Validation error — review input parameters. |
401 |
Unauthorized. Authentication failed or missing credentials. |
403 |
Forbidden. The user does not have the necessary permissions. |
404 |
Not Found. The requested resource could not be located. |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. Please slow down and retry after some time. |
500 |
Internal Server Error. An unexpected condition prevented the request from being fulfilled. |
InvitationStatus Enum
The InvitationStatus
enum is used to represent the status of a team member's invitation.
This enum provides two possible states for a team member's invitation:
ACCEPTED
: The team member has accepted the invitation and is active in the workspace.PENDING
: The invitation has been sent but not yet accepted by the team member.
Invite Team Members
Invites multiple team members to join a workspace with specified accessLevels.
HTTP Request
https://api.instapage.com/v1/workspaces/{workspaceId}/team-members
curl -X POST \
https://api.instapage.com/v1/workspaces/{workspaceId}/team-members \
-H 'Authorization: Bearer {token}' \
-H 'Content-Type: application/json' \
-d '[
{
"email": "user1@example.com",
"accessLevel": "viewer"
},
{
"email": "user2@example.com",
"accessLevel": "manager"
}
]'
async function inviteTeamMembers(workspaceId, teamMembers, apiKey) {
const response = await fetch(
`https://api.instapage.com/v1/workspaces/${workspaceId}/team-members`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(teamMembers)
}
);
if (!response.ok) {
const errorData = await response.json();
throw new Error(`Failed to invite team members: ${errorData.details}`);
}
return response.json();
}
// Example usage
const teamMembers = [
{ email: "user1@example.com", accessLevel: "viewer" },
{ email: "user2@example.com", accessLevel: "manager" }
];
try {
await inviteTeamMembers(workspaceId, teamMembers, apiKey);
console.log('Team members invited successfully');
} catch (error) {
console.error('Error inviting team members:', error);
}
type AccessLevel = 'viewer' | 'editor' | 'manager';
interface TeamMemberInvite {
email: string;
accessLevel: AccessLevel;
}
async function inviteTeamMembers(
workspaceId: number,
teamMembers: TeamMemberInvite[],
apiKey: string
): Promise<void> {
const response = await fetch(
`https://api.instapage.com/v1/workspaces/${workspaceId}/team-members`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(teamMembers)
}
);
if (!response.ok) {
const errorData = await response.json();
throw new Error(`Failed to invite team members: ${errorData.details}`);
}
return response.json();
}
// Example usage
const teamMembers: TeamMemberInvite[] = [
{ email: "user1@example.com", accessLevel: "viewer" },
{ email: "user2@example.com", accessLevel: "manager" }
];
try {
await inviteTeamMembers(workspaceId, teamMembers, apiKey);
console.log('Team members invited successfully');
} catch (error) {
console.error('Error inviting team members:', error);
}
<?php
declare(strict_types=1);
namespace InstapageTeamMembers;
enum AccessLevel: string {
case Viewer = 'viewer';
case Editor = 'editor';
case Manager = 'manager';
}
readonly class TeamMemberInvite {
public function __construct(
public string $email,
public AccessLevel $accessLevel
) {}
public function toArray(): array {
return [
'email' => $this->email,
'accessLevel' => $this->accessLevel->value
];
}
}
class TeamMemberInviter {
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
/**
* @param TeamMemberInvite[] $teamMembers
*/
public function inviteTeamMembers(
int $workspaceId,
array $teamMembers
): void {
$url = sprintf(
'%s/v1/workspaces/%d/team-members',
$this->baseUrl,
$workspaceId
);
$requestBody = array_map(
fn($member) => $member->toArray(),
$teamMembers
);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
],
CURLOPT_POSTFIELDS => json_encode($requestBody)
]);
$response = curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($statusCode !== 201) {
$errorData = json_decode($response, true);
throw new \RuntimeException(
"Failed to invite team members: " .
($errorData['details'] ?? 'Unknown error')
);
}
}
}
// Example usage
try {
$inviter = new TeamMemberInviter(
'https://api.instapage.com',
'your_api_key_here'
);
$teamMembers = [
new TeamMemberInvite('user1@example.com', AccessLevel::Viewer),
new TeamMemberInvite('user2@example.com', AccessLevel::Manager)
];
$inviter->inviteTeamMembers(123, $teamMembers);
echo "Team members invited successfully\n";
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
The ID of the workspace to invite members to |
Request Body
The request body should be an array of objects with the following structure:
Parameter | Type | Description |
---|---|---|
email |
string |
Email address of the user to invite |
accessLevel |
string |
accessLevel to assign to the user (viewer , viewer , or manager ) |
Response Status Codes
Status | Description |
---|---|
201 |
Created. The team members were successfully invited. |
400 |
Bad Request. Invalid input parameters or email format. |
401 |
Unauthorized. Authentication failed. |
403 |
Forbidden. User doesn't have permission to invite members or team member limit exceeded. |
404 |
Not Found. The workspace could not be found. |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. Please slow down and retry after some time. |
500 |
Internal Server Error. An unexpected error occurred. |
Remove Team Members
Remove one or more team members from a workspace. This endpoint supports bulk removal operations.
HTTP Request
https://api.instapage.com/v1/workspaces/{workspaceId}/team-members
curl -X DELETE \
"https://api.instapage.com/v1/workspaces/{workspaceId}/team-members" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '[
{
"email": "user1@example.com"
},
{
"email": "user2@example.com"
}
]'
Example of error response:
{
"title": "InvalidArgumentException",
"details": "Email \"owner@example.com\" can't be removed from workspace because is owner",
"meta": {
"requestedUserId": 4373,
"requestedPageId": 9910,
"requestedWorkspaceId": 7068
}
}
async function removeTeamMembers(workspaceId, emails, apiKey) {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/team-members`;
const response = await fetch(url, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(emails.map(email => ({ email })))
});
if (response.status === 201) {
console.log('Team members removed successfully');
return;
}
const errorData = await response.json();
throw new Error(`Failed to remove team members: ${errorData.details}`);
}
interface RemoveTeamMemberRequest {
email: string;
}
interface ErrorResponse {
title: string;
details: string;
meta: {
requestedUserId: number;
requestedWorkspaceId: number;
};
}
async function removeTeamMembers(
workspaceId: number,
emails: string[],
apiKey: string
): Promise<void> {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/team-members`;
const request: RemoveTeamMemberRequest[] = emails.map(email => ({ email }));
const response = await fetch(url, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(request)
});
if (response.status === 201) {
return;
}
const errorData: ErrorResponse = await response.json();
throw new Error(`Failed to remove team members: ${errorData.details}`);
}
<?php
declare(strict_types=1);
namespace InstapageTeamMemberRemoval;
readonly class RemoveTeamMemberRequest {
public function __construct(
public string $email
) {}
public function toArray(): array {
return [
'email' => $this->email
];
}
}
readonly class ErrorResponse {
public function __construct(
public string $title,
public string $details,
public int $requestedUserId,
public int $requestedWorkspaceId
) {}
public static function fromArray(array $data): self {
return new self(
$data['title'],
$data['details'],
$data['meta']['requestedUserId'],
$data['meta']['requestedWorkspaceId']
);
}
}
class TeamMembersRemover {
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
/**
* @param string[] $emails
* @throws \RuntimeException
*/
public function removeTeamMembers(int $workspaceId, array $emails): void {
$url = sprintf(
'%s/v1/workspaces/%d/team-members',
$this->baseUrl,
$workspaceId
);
$request = array_map(
fn(string $email) => (new RemoveTeamMemberRequest($email))->toArray(),
$emails
);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
],
CURLOPT_POSTFIELDS => json_encode($request)
]);
$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new \RuntimeException('CURL Error: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode === 201) {
return;
}
$errorData = json_decode($responseBody, true);
$error = ErrorResponse::fromArray($errorData);
throw new \RuntimeException("Failed to remove team members: {$error->details}");
}
}
// Example usage
try {
$remover = new TeamMembersRemover(
'https://api.instapage.com',
'your_api_key_here'
);
$workspaceId = 1234;
$emails = [
'user1@example.com',
'user2@example.com'
];
$remover->removeTeamMembers($workspaceId, $emails);
echo "Team members removed successfully\n";
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
The ID of the workspace to remove members from |
Request Body
The request body should be an array of objects, each containing:
Parameter | Type | Description |
---|---|---|
email |
string |
Email address of the team member |
Special Cases
- If the authenticated user is in the removal list and is not the workspace owner, they will leave the workspace
- If the workspace owner's email is included in the removal list, an error will be returned
- If any email in the list doesn't belong to a team member, an error will be returned
Response Status Codes
Status | Description |
---|---|
201 |
Created. Team members were successfully removed. |
400 |
Bad Request. Invalid input parameters or attempting to remove workspace owner. |
401 |
Unauthorized. Authentication failed or missing credentials. |
403 |
Forbidden. User doesn't have necessary permissions (must be owner or manager). |
404 |
Not Found. Workspace not found. |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. Please slow down and retry after some time. |
500 |
Internal Server Error. An unexpected condition prevented the request from being fulfilled. |
Error Responses
The API returns error responses in the following format:
JSON Path | Type | Description |
---|---|---|
title |
string |
The type of error that occurred |
details |
string |
A human-readable description of the error |
meta.requestedUserId |
number |
ID of the user who made the request |
meta.requestedWorkspaceId |
number |
ID of the workspace involved |
Change Team Member Roles
Updates the roles of existing team members in a workspace. This endpoint allows bulk role updates for multiple team members simultaneously.
HTTP Request
https://api.instapage.com/v1/workspaces/{workspaceId}/team-members
curl -X PUT \
"https://api.instapage.com/v1/workspaces/{workspaceId}/team-members" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '[
{
"email": "user1@example.com",
"targetAccessLevel": "viewer"
},
{
"email": "user2@example.com",
"targetAccessLevel": "viewer"
}
]'
async function updateTeamMemberRoles(workspaceId, roleUpdates, apiKey) {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/team-members`;
const response = await fetch(url, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(roleUpdates)
});
if (response.status === 201) {
console.log('Team member roles updated successfully');
} else {
const errorData = await response.json();
throw new Error(`Failed to update roles: ${errorData.details}`);
}
}
// Example usage
const roleUpdates = [
{
email: "user1@example.com",
targetAccessLevel: "viewer"
},
{
email: "user2@example.com",
targetAccessLevel: "viewer"
}
];
try {
await updateTeamMemberRoles(workspaceId, roleUpdates, apiKey);
} catch (error) {
console.error('Error updating team member roles:', error);
}
type AccessLevel = 'viewer' | 'editor' | 'manager';
interface RoleUpdate {
email: string;
targetAccessLevel: AccessLevel;
}
interface ErrorResponse {
title: string;
details: string;
meta: {
requestedUserId: number;
requestedWorkspaceId: number;
};
}
async function updateTeamMemberRoles(
workspaceId: number,
roleUpdates: RoleUpdate[],
apiKey: string
): Promise<void> {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/team-members`;
const response = await fetch(url, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(roleUpdates)
});
if (response.status === 201) {
console.log('Team member roles updated successfully');
return;
}
const errorData: ErrorResponse = await response.json();
throw new Error(`Failed to update roles: ${errorData.details}`);
}
// Example usage
const roleUpdates: RoleUpdate[] = [
{
email: "user1@example.com",
targetAccessLevel: "viewer"
},
{
email: "user2@example.com",
targetAccessLevel: "editor"
}
];
try {
await updateTeamMemberRoles(workspaceId, roleUpdates, apiKey);
} catch (error) {
console.error('Error updating team member roles:', error);
}
<?php
declare(strict_types=1);
namespace InstapageTeamMembers;
enum AccessLevel: string
{
case Viewer = 'viewer';
case Editor = 'editor';
case Manager = 'manager';
}
readonly class RoleUpdate
{
public function __construct(
public string $email,
public AccessLevel $targetAccessLevel
) {}
public function toArray(): array
{
return [
'email' => $this->email,
'targetAccessLevel' => $this->targetAccessLevel->value
];
}
}
class TeamMemberRoleUpdater
{
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
/**
* @param RoleUpdate[] $roleUpdates
* @throws \RuntimeException
*/
public function updateRoles(int $workspaceId, array $roleUpdates): void
{
$url = sprintf(
'%s/v1/workspaces/%d/team-members',
$this->baseUrl,
$workspaceId
);
$requestBody = array_map(
fn(RoleUpdate $update) => $update->toArray(),
$roleUpdates
);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_POSTFIELDS => json_encode($requestBody),
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
]
]);
$response = curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($statusCode === 201) {
return;
}
$errorData = json_decode($response, true);
throw new \RuntimeException(
sprintf('Failed to update roles: %s', $errorData['details'])
);
}
}
// Example usage
try {
$updater = new TeamMemberRoleUpdater(
'https://api.instapage.com',
'your_api_key_here'
);
$roleUpdates = [
new RoleUpdate('user1@example.com', AccessLevel::Viewer),
new RoleUpdate('user2@example.com', AccessLevel::Editor)
];
$updater->updateRoles(7068, $roleUpdates);
echo "Team member roles updated successfully\n";
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage() . "\n";
}
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
The ID of the workspace containing the team members |
Request Body
The request body should be an array of objects with the following structure:
Parameter | Type | Description |
---|---|---|
email |
string |
Email address of the team member to update |
targetAccessLevel |
string |
New role to assign (viewer , editor , or manager ) |
Response Status Codes
Status | Description |
---|---|
201 |
Created. The team member roles were successfully updated. |
400 |
Bad Request. Invalid input parameters or duplicate emails in request. |
401 |
Unauthorized. Authentication failed or missing credentials. |
403 |
Forbidden. The user doesn't have permission to modify roles or attempting to modify the owner's role. |
404 |
Not Found. The workspace was not found or one or more team members don't exist in the workspace. |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. Please slow down and retry after some time. |
500 |
Internal Server Error. An unexpected condition prevented the request from being fulfilled. |
Error Responses
The API may return error responses in the following format:
JSON Path | Type | Description |
---|---|---|
title |
string |
The type of error that occurred |
details |
string |
A human-readable description of the error |
meta.requestedUserId |
number |
ID of the user who made the request |
meta.requestedWorkspaceId |
number |
ID of the workspace involved |
Related articles
Pages
Get All Pages
Retrieve pages for a specific workspace.
HTTP Request
https://api.instapage.com/v1/workspaces/{workspaceId}/pages
curl "https://api.instapage.com/v1/workspaces/{workspaceId}/pages" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
The above request returns JSON structured like this:
{
"data": [
{
"id": 9399,
"title": "My published page",
"url": "mypage.example.com",
"publishStatus": "published",
"publishMethod": "customDomain",
"createdAt": 1692168365,
"updatedAt": 1725361407,
"publishedAt": 1726005213,
"isDeleted": false,
"pageType": "standard",
"totalPersonalizedExperienceCount": 6,
"isScheduled": false,
"groupId": 1234
},
{
"id": 9909,
"title": "My unpublished page",
"url": null,
"publishStatus": "unpublished",
"publishMethod": "customDomain",
"createdAt": 1725916359,
"updatedAt": 1725916362,
"publishedAt": 1725921862,
"isDeleted": false,
"pageType": "standard",
"totalPersonalizedExperienceCount": 0,
"isScheduled": true,
"groupId": null
}
],
"meta": {
"pagination": {
"currentPage": 1,
"perPage": 100,
"totalItemsCount": 2,
"totalPagesCount": 1,
"nextPage": null,
"previousPage": null
}
}
}
async function fetchPages(workspaceId, page, isDeleted, publishStatus, apiKey) {
const url = new URL(`https://api.instapage.com/v1/workspaces/${workspaceId}/pages`);
url.searchParams.append('page', page);
url.searchParams.append('isDeleted', isDeleted);
if (publishStatus) {
url.searchParams.append('publishStatus', publishStatus);
}
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
});
if (!response.ok) {
throw new Error(`Failed to fetch pages: ${response.statusText}`);
}
const data = await response.json();
return data;
}
type Page = {
id: number;
title: string;
url: string | null;
publishStatus: 'published' | 'unpublished' | 'publishedHasChanges';
publishMethod: 'cmsPlugin' | 'customDomain' | 'pageDemo';
createdAt: number;
updatedAt: number;
isDeleted: boolean;
pageType: 'amp' | 'standard' | 'collectionTemplate' | 'wordpress' | 'drupal';
totalPersonalizedExperienceCount: number;
isScheduled: boolean;
groupId: number | null;
};
type PaginationMeta = {
currentPage: number;
perPage: number;
totalItemsCount: number;
totalPagesCount: number;
nextPage: number | null;
previousPage: number | null;
};
type PagesResponse = {
data: Page[];
meta: {
pagination: PaginationMeta;
};
};
async function fetchPages(
apiKey: string,
workspaceId: number,
page: number,
isDeleted: boolean,
publishStatus?: 'published' | 'unpublished' | 'publishedHasChanges',
): Promise<PagesResponse> {
const url = new URL(`https://api.instapage.com/v1/workspaces/${workspaceId}/pages`);
url.searchParams.append('page', page.toString());
url.searchParams.append('isDeleted', isDeleted.toString());
if (publishStatus) {
url.searchParams.append('publishStatus', publishStatus);
}
const response = await fetch(url.toString(), {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
});
if (!response.ok) {
throw new Error(`Failed to fetch pages: ${response.statusText}`);
}
const data: PagesResponse = await response.json();
return data;
}
<?php
declare(strict_types=1);
namespace InstapagePagesFetcher;
enum PublishStatus: string
{
case Published = 'published';
case Unpublished = 'unpublished';
case PublishedHasChanges = 'publishedHasChanges';
}
enum PublishMethod: string
{
case CmsPlugin = 'cmsPlugin';
case CustomDomain = 'customDomain';
case PageDemo = 'pageDemo';
}
enum PageType: string
{
case Amp = 'amp';
case Standard = 'standard';
case CollectionTemplate = 'collectionTemplate';
case WordPress = 'wordpress';
case Drupal = 'drupal';
}
readonly class Page
{
public function __construct(
public int $id,
public string $title,
public ?string $url,
public PublishStatus $publishStatus,
public PublishMethod $publishMethod,
public int $createdAt,
public int $updatedAt,
public bool $isDeleted,
public PageType $pageType,
public int $totalPersonalizedExperienceCount,
public bool $isScheduled,
public ?int $groupId,
) {}
public function toArray(): array
{
return [
'id' => $this->id,
'title' => $this->title,
'url' => $this->url,
'publishStatus' => $this->publishStatus->value,
'publishMethod' => $this->publishMethod->value,
'createdAt' => $this->createdAt,
'updatedAt' => $this->updatedAt,
'isDeleted' => $this->isDeleted,
'pageType' => $this->pageType->value,
'totalPersonalizedExperienceCount' => $this->totalPersonalizedExperienceCount,
'isScheduled' => $this->isScheduled,
'groupId' => $this->groupId,
];
}
}
readonly class PaginationMeta
{
public function __construct(
public int $currentPage,
public int $perPage,
public int $totalItemsCount,
public int $totalPagesCount,
public ?int $nextPage,
public ?int $previousPage
) {}
public function toArray(): array
{
return [
'currentPage' => $this->currentPage,
'perPage' => $this->perPage,
'totalItemsCount' => $this->totalItemsCount,
'totalPagesCount' => $this->totalPagesCount,
'nextPage' => $this->nextPage,
'previousPage' => $this->previousPage
];
}
}
class PagesResponse
{
/**
* @param Page[] $data
*/
public function __construct(
private array $data,
private PaginationMeta $paginationMeta
) {}
public function getData(): array
{
return $this->data;
}
public function getMeta(): PaginationMeta
{
return $this->paginationMeta;
}
}
class PagesFetcher
{
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
public function fetchPages(
int $workspaceId,
int $page,
bool $isDeleted,
?PublishStatus $publishStatus = null
): PagesResponse {
$url = sprintf(
'%s/v1/workspaces/%d/pages?page=%d&isDeleted=%s%s',
$this->baseUrl,
$workspaceId,
$page,
$isDeleted ? 'true' : 'false',
$publishStatus ? '&publishStatus=' . $publishStatus->value : ''
);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
]
]);
$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new \RuntimeException('CURL Error: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode !== 200) {
throw new \RuntimeException("API request failed with status code: $httpCode");
}
$responseData = json_decode($responseBody, true);
return new PagesResponse(
array_map(fn($pageData) => new Page(
$pageData['id'],
$pageData['title'],
$pageData['url'] ?? null,
PublishStatus::from($pageData['publishStatus']),
PublishMethod::from($pageData['publishMethod']),
$pageData['createdAt'],
$pageData['updatedAt'],
$pageData['isDeleted'],
PageType::from($pageData['pageType']),
$pageData['totalPersonalizedExperienceCount'],
$pageData['isScheduled'],
$pageData['groupId'],
), $responseData['data']),
new PaginationMeta(
$responseData['meta']['pagination']['currentPage'],
$responseData['meta']['pagination']['perPage'],
$responseData['meta']['pagination']['totalItemsCount'],
$responseData['meta']['pagination']['totalPagesCount'],
$responseData['meta']['pagination']['nextPage'],
$responseData['meta']['pagination']['previousPage']
)
);
}
}
// Example usage
try {
$baseUrl = 'https://api.instapage.com';
$apiKey = 'your_api_key_here';
$workspaceId = 1234;
$fetcher = new PagesFetcher($baseUrl, $apiKey);
$pagesResponse = $fetcher->fetchPages(
workspaceId: $workspaceId,
page: 1,
isDeleted: false,
publishStatus: PublishStatus::Published
);
// Process pages
foreach ($pagesResponse->getData() as $page) {
// Do something with each page
print_r($page->toArray());
}
// Access pagination metadata
$paginationMeta = $pagesResponse->getMeta();
print_r($paginationMeta->toArray());
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
The ID of the workspace to retrieve pages for |
Query Parameters
Parameter | Type | Default | Description |
---|---|---|---|
page |
number |
1 |
Specifies which page to fetch. Used for pagination purposes. |
isDeleted |
boolean |
false |
When set to true , returns only deleted pages. When false , returns only non-deleted pages. |
publishStatus |
string? |
null |
Filter pages by publish status. Possible values: published , unpublished , publishedHasChanges |
publishMethod |
string? |
null |
Filter pages by publish method. Possible values: cmsPlugin , customDomain , pageDemo |
publishedAfter |
number? |
null |
Filter pages published after the specified UNIX timestamp. |
publishedBefore |
number? |
null |
Filter pages published before the specified UNIX timestamp. |
createdAfter |
number? |
null |
Filter pages created after the specified UNIX timestamp. |
createdBefore |
number? |
null |
Filter pages created before the specified UNIX timestamp. |
withGroupId |
number? |
Filter pages by group - If parameter is not set: Returns all pages - If parameter is set to a number: Returns only pages with matching groupId - If parameter is set to 'null': Returns only pages with null groupId |
This endpoint retrieves a list of pages for the specified workspace. The response includes detailed information about
each page, such as its ID, title, URL, publish status, and more. The results are paginated, and you can use the page
query parameter to navigate through the pages of results.
Response JSON structure
JSON Path | Type | Description |
---|---|---|
data[].id |
number |
Unique identifier of the page. |
data[].title |
string |
Title of the page. |
data[].url |
string? |
URL of the page, null if unpublished. |
data[].publishStatus |
string |
Current publish status of the page (e.g., published , unpublished ). |
data[].publishMethod |
string |
Method used for publishing the page (e.g., customDomain ). |
data[].createdAt |
number |
Creation timestamp of the page (UNIX format). |
data[].updatedAt |
number |
Last update timestamp of the page (UNIX format). |
data[].publishedAt |
number? |
Timestamp of when the page was published (UNIX format), null if unpublished. |
data[].isDeleted |
boolean |
Indicates whether the page has been deleted. |
data[].pageType |
string |
Type of the page (e.g., standard ). |
data[].totalPersonalizedExperienceCount |
number |
Total count of personalized experiences for the page. |
data[].isScheduled |
boolean |
Indicates whether the page is scheduled for publication. |
data[].groupId |
number? |
Id of Group |
meta.pagination.currentPage |
number |
Number of the current page of results. |
meta.pagination.perPage |
number |
Number of items per page. |
meta.pagination.totalItemsCount |
number |
Total number of items. |
meta.pagination.totalPagesCount |
number |
Total number of pages. |
meta.pagination.nextPage |
number? |
Number of the next page, or null if there is no next page. |
meta.pagination.previousPage |
number? |
Number of the previous page, or null if there is no previous page. |
Response status codes
Status | Description |
---|---|
200 |
The request was processed successfully. |
400 |
Bad request. Validation error — review input parameters. |
401 |
Unauthorized. Authentication failed or missing credentials. |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. Please slow down and retry after some time. |
500 |
Internal Server Error. An unexpected condition prevented the request from being fulfilled. |
Get Page
Retrieve detailed information about a specific page within a workspace.
HTTP Request
https://api.instapage.com/v1/workspaces/{workspaceId}/pages/{pageId}
curl "https://api.instapage.com/v1/workspaces/$workspaceId/pages/$pageId" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json"
async function fetchPageDetails(workspaceId, pageId, apiKey) {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/pages/${pageId}`;
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`Failed to fetch page details: ${response.statusText}`);
}
return await response.json();
}
type PublishStatus = 'published' | 'unpublished' | 'publishedHasChanges';
type PublishMethod = 'cmsPlugin' | 'customDomain' | 'pageDemo';
type PageType = 'amp' | 'standard' | 'collectionTemplate' | 'wordpress' | 'drupal';
interface PageResponse {
data: {
id: number;
title: string;
url: string | null;
publishStatus: PublishStatus;
publishMethod: PublishMethod | null;
createdAt: number;
updatedAt: number;
publishedAt: number | null;
isDeleted: boolean;
pageType: PageType;
totalPersonalizedExperienceCount: number;
isScheduled: boolean;
groupId: number | number;
};
meta: Record<string, never>;
}
async function fetchPageDetails(
workspaceId: number,
pageId: number,
apiKey: string
): Promise<PageResponse> {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/pages/${pageId}`;
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`Failed to fetch page details: ${response.statusText}`);
}
return await response.json();
}
<?php
declare(strict_types=1);
namespace InstapagePageFetcher;
enum PublishStatus: string {
case Published = 'published';
case Unpublished = 'unpublished';
case PublishedHasChanges = 'publishedHasChanges';
}
enum PublishMethod: string {
case CmsPlugin = 'cmsPlugin';
case CustomDomain = 'customDomain';
case PageDemo = 'pageDemo';
}
enum PageType: string {
case Amp = 'amp';
case Standard = 'standard';
case CollectionTemplate = 'collectionTemplate';
case WordPress = 'wordpress';
case Drupal = 'drupal';
}
readonly class PageDetails {
public function __construct(
public int $id,
public string $title,
public ?string $url,
public PublishStatus $publishStatus,
public ?PublishMethod $publishMethod,
public int $createdAt,
public int $updatedAt,
public ?int $publishedAt,
public bool $isDeleted,
public PageType $pageType,
public int $totalPersonalizedExperienceCount,
public bool $isScheduled,
public ?int $groupId,
) {}
public static function fromArray(array $data): self {
return new self(
$data['id'],
$data['title'],
$data['url'],
PublishStatus::from($data['publishStatus']),
$data['publishMethod'] ? PublishMethod::from($data['publishMethod']) : null,
$data['createdAt'],
$data['updatedAt'],
$data['publishedAt'],
$data['isDeleted'],
PageType::from($data['pageType']),
$data['totalPersonalizedExperienceCount'],
$data['isScheduled'],
$data['groupId'],
);
}
}
class PageDetailsFetcher {
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
public function fetchPageDetails(int $workspaceId, int $pageId): PageDetails {
$url = sprintf(
'%s/v1/workspaces/%d/pages/%d',
$this->baseUrl,
$workspaceId,
$pageId
);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
]
]);
$response = curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($statusCode !== 200) {
throw new \RuntimeException("Failed to fetch page details: HTTP $statusCode");
}
$data = json_decode($response, true);
return PageDetails::fromArray($data['data']);
}
}
// Example usage
try {
$fetcher = new PageDetailsFetcher(
'https://api.instapage.com',
'your_api_key_here'
);
$pageDetails = $fetcher->fetchPageDetails(123, 456);
var_dump($pageDetails);
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
The above request returns JSON structured like this:
{
"data": {
"id": 9371,
"title": "My Landing Page",
"url": "landing.example.com",
"publishStatus": "published",
"publishMethod": "customDomain",
"createdAt": 1692168365,
"updatedAt": 1725361407,
"publishedAt": 1726005213,
"isDeleted": false,
"pageType": "standard",
"totalPersonalizedExperienceCount": 3,
"isScheduled": false,
"groupId": 123
},
"meta": {}
}
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
The ID of the workspace containing the page |
pageId |
number |
The ID of the page to retrieve |
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Response JSON Structure
JSON Path | Type | Description |
---|---|---|
data.id |
number |
Unique identifier of the page |
data.title |
string |
Title of the page |
data.url |
string? |
URL where the page is published (null if unpublished) |
data.publishStatus |
string |
Current publish status (published , unpublished , publishedHasChanges ) |
data.publishMethod |
string? |
Method used for publishing (cmsPlugin , customDomain , pageDemo ) |
data.createdAt |
number |
Creation timestamp (UNIX format) |
data.updatedAt |
number |
Last update timestamp (UNIX format) |
data.publishedAt |
number? |
Publication timestamp (UNIX format), null if unpublished |
data.isDeleted |
boolean |
Indicates if the page has been deleted |
data.pageType |
string |
Type of page (amp , standard , collectionTemplate , wordpress , drupal ) |
data.totalPersonalizedExperienceCount |
number |
Number of personalized experiences for this page |
data.isScheduled |
boolean |
Indicates if the page has scheduled publishing |
data.groupId |
number? |
Id of group |
Response Status Codes
Status | Description |
---|---|
200 |
The request was processed successfully |
400 |
Bad request. Invalid page ID or workspace ID |
401 |
Unauthorized. Authentication failed or missing credentials |
404 |
Not Found. The page or workspace could not be found or the page does not belong to workspace |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. Please slow down and retry after some time. |
500 |
Internal Server Error. An unexpected condition prevented the request from being fulfilled |
Publish a Page
Publishes a page with the specified publication method and target URL.
HTTP Request
https://api.instapage.com/v1/workspaces/{workspaceId}/pages/{pageId}/publication
curl -X POST \
https://api.instapage.com/v1/workspaces/{workspaceId}/pages/{pageId}/publication \
-H 'Authorization: Bearer {token}' \
-H 'Content-Type: application/json' \
-d '{
"targetUrl": {targetUrl},
"publicationMethod": {publicationMethod}
}'
> Example of body error from request
{
"title": "InvalidDomainException",
"details": "The provided domain is not valid",
"meta": {
"requestedUserId": 4373,
"requestedPageId": 9910,
"requestedWorkspaceId": 7068,
"requestedPublishMethod": "customDomain"
}
}
const publishPage = async (targetUrl, publicationMethod, pageId, workspaceId, token) => {
try {
const response = await fetch(`https://api.instapage.com/v1/workspaces/${workspaceId}/pages/${pageId}/publication`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
targetUrl: targetUrl,
publicationMethod: publicationMethod,
})
});
if (response.status === 202) {
console.log('Page publication request accepted');
} else {
const errorData = await response.json();
console.error('Error publishing page:', errorData);
}
} catch (error) {
console.error('An error occurred:', error);
}
};
type PluginPublicationMethod = 'wordpress' | 'drupal';
type DomainPublicationMethod = 'customDomain' | 'freeDomain';
type PublicationMethod = PluginPublicationMethod | DomainPublicationMethod;
type PublishPageBodyRequest<T extends PublicationMethod> = T extends PluginPublicationMethod
? {
targetUrl: null;
publicationMethod: T;
}
: {
targetUrl: string;
publicationMethod: T;
};
interface ErrorResponse {
title: string;
details: string;
meta: {
requestedUserId: number;
requestedPageId: number;
requestedWorkspaceId: number;
requestedPublishMethod: string;
};
}
const publishPage = async (request: PublishPageBodyRequest, pageId: number, workspaceId: number, token: string): Promise<void> => {
try {
const response = await fetch(`https://api.instapage.com/v1/workspaces/${workspaceId}/pages/${pageId}/publication`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(request)
});
if (response.status === 202) {
console.log('Page publication request accepted');
} else {
const errorData: ErrorResponse = await response.json();
console.error('Error publishing page:', errorData);
}
} catch (error) {
console.error('An error occurred:', error);
}
};
<?php
declare(strict_types=1);
namespace InstapagePagePublication;
enum PluginPublicationMethod: string
{
case WordPress = 'wordpress';
case Drupal = 'drupal';
}
enum DomainPublicationMethod: string
{
case CustomDomain = 'customDomain';
case FreeDomain = 'freeDomain';
}
class PublicationMethodValidator
{
public static function validate(string $method): bool
{
return
in_array($method, array_column(PluginPublicationMethod::cases(), 'value')) ||
in_array($method, array_column(DomainPublicationMethod::cases(), 'value'));
}
}
class PublishPageBodyRequest
{
public function __construct(
private ?string $targetUrl,
private string $publicationMethod
) {
$this->validate();
}
private function validate(): void
{
// Validate publication method
if (!PublicationMethodValidator::validate($this->publicationMethod)) {
throw new \InvalidArgumentException("Invalid publication method");
}
// Validate target URL based on publication method
$isPluginMethod = in_array(
$this->publicationMethod,
array_column(PluginPublicationMethod::cases(), 'value')
);
if ($isPluginMethod && $this->targetUrl !== null) {
throw new \InvalidArgumentException("Plugin publication method cannot have a target URL");
}
if (!$isPluginMethod && $this->targetUrl === null) {
throw new \InvalidArgumentException("Non-plugin publication method requires a target URL");
}
}
public function toArray(): array
{
return [
'targetUrl' => $this->targetUrl,
'publicationMethod' => $this->publicationMethod
];
}
}
readonly class ErrorResponse
{
public function __construct(
public string $title,
public string $details,
public int $requestedUserId,
public int $requestedPageId,
public int $requestedWorkspaceId,
public string $requestedPublishMethod
) {}
public static function fromArray(array $data): self
{
return new self(
$data['title'],
$data['details'],
$data['meta']['requestedUserId'],
$data['meta']['requestedPageId'],
$data['meta']['requestedWorkspaceId'],
$data['meta']['requestedPublishMethod']
);
}
public function toArray(): array
{
return [
'title' => $this->title,
'details' => $this->details,
'meta' => [
'requestedUserId' => $this->requestedUserId,
'requestedPageId' => $this->requestedPageId,
'requestedWorkspaceId' => $this->requestedWorkspaceId,
'requestedPublishMethod' => $this->requestedPublishMethod
]
];
}
}
class PagePublisher
{
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
public function publishPage(
PublishPageBodyRequest $request,
int $pageId,
int $workspaceId
): void {
$url = sprintf(
'%s/v1/workspaces/%d/pages/%d/publication',
$this->baseUrl,
$workspaceId,
$pageId
);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
],
CURLOPT_POSTFIELDS => json_encode($request->toArray())
]);
$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new \RuntimeException('CURL Error: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode === 202) {
echo "Page publication request accepted\n";
return;
}
if ($httpCode !== 200) {
$errorData = json_decode($responseBody, true);
$errorResponse = ErrorResponse::fromArray($errorData);
throw new \RuntimeException(
"Error publishing page: " .
json_encode($errorResponse->toArray())
);
}
}
}
// Example usage
try {
$baseUrl = 'https://api.instapage.com';
$apiKey = 'your_api_key_here';
$workspaceId = 1234;
$pageId = 5678;
$publisher = new PagePublisher($baseUrl, $apiKey);
// Example for plugin publication method (no target URL)
$pluginRequest = new PublishPageBodyRequest(
targetUrl: null,
publicationMethod: PluginPublicationMethod::WordPress->value
);
$publisher->publishPage($pluginRequest, $pageId, $workspaceId);
// Example for domain publication method (with target URL)
$domainRequest = new PublishPageBodyRequest(
targetUrl: 'https://example.com',
publicationMethod: DomainPublicationMethod::CustomDomain->value
);
$publisher->publishPage($domainRequest, $pageId, $workspaceId);
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
The ID of the workspace |
pageId |
number |
The ID of the page to publish |
Request Body
Parameter | Type | Description |
---|---|---|
targetUrl |
string? |
The URL where the page will be published, if is wordpress or drupal should be null |
publicationMethod |
string |
The method of publication. Can be wordpress , drupal , customDomain , or freeDomain |
Responses
Status | Description |
---|---|
202 |
Accepted. The request has been accepted for processing. |
400 |
Bad Request. Invalid input parameters or publication method not permitted. |
401 |
Unauthorized. Authentication failed (implied by AuthMiddleware). |
403 |
Forbidden. Published pages limit exceeded or user doesn't have necessary permissions. |
404 |
Not Found. The specified page was not found. |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. Please slow down and retry after some time. |
500 |
Internal Server Error. An unexpected error occurred or publish setup not found. |
503 |
Service Unavailable. Entity-related exception occurred. |
Unpublish a Page
Unpublishes a previously published page.
HTTP Request
https://api.instapage.com/v1/workspaces/{workspaceId}/pages/{pageId}/publication
curl -X DELETE \
https://api.instapage.com/v1/workspaces/{workspaceId}/pages/{pageId}/publication \
-H 'Authorization: Bearer {token}' \
-H 'Content-Type: application/json'
Example of body error from request
{
"title": "PageIsNotPublishedException",
"details": "The page \"9910\" is not published",
"meta": {
"requestedUserId": 4373,
"requestedPageId": 9910,
"requestedWorkspaceId": 7068
}
}
const unpublishPage = async (pageId, workspaceId, token) => {
const response = await fetch(`https://api.instapage.com/v1/workspaces/${workspaceId}/pages/${pageId}/publication`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
if (response.status === 201) {
console.log('Page unpublished successfully');
} else {
const errorData = await response.json();
console.error('Error unpublishing page:', errorData);
}
};
interface ErrorResponse {
title: string;
details: string;
meta: {
requestedUserId: number;
requestedPageId: number;
requestedWorkspaceId: number;
};
}
const unpublishPage = async (pageId: number, workspaceId: number, token: string) => {
try {
const response = await fetch(`https://api.instapage.com/v1/workspaces/${workspaceId}/pages/${pageId}/publication`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
if (response.status === 201) {
console.log('Page unpublished successfully');
} else {
const errorData: ErrorResponse = await response.json();
console.error('Error unpublishing page:', errorData);
}
} catch (error) {
console.error('An error occurred:', error);
}
};
<?php
declare(strict_types=1);
namespace InstapagePageUnpublication;
readonly class ErrorResponse
{
public function __construct(
public string $title,
public string $details,
public int $requestedUserId,
public int $requestedPageId,
public int $requestedWorkspaceId
) {}
public static function fromArray(array $data): self
{
return new self(
$data['title'],
$data['details'],
$data['meta']['requestedUserId'],
$data['meta']['requestedPageId'],
$data['meta']['requestedWorkspaceId']
);
}
public function toArray(): array
{
return [
'title' => $this->title,
'details' => $this->details,
'meta' => [
'requestedUserId' => $this->requestedUserId,
'requestedPageId' => $this->requestedPageId,
'requestedWorkspaceId' => $this->requestedWorkspaceId
]
];
}
}
class PageUnpublisher
{
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
public function unpublishPage(int $pageId, int $workspaceId): void
{
$url = sprintf(
'%s/v1/workspaces/%d/pages/%d/publication',
$this->baseUrl,
$workspaceId,
$pageId
);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
]
]);
$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new \RuntimeException('CURL Error: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode === 201) {
echo "Page unpublished successfully\n";
return;
}
if ($httpCode !== 200) {
$errorData = json_decode($responseBody, true);
$errorResponse = ErrorResponse::fromArray($errorData);
throw new \RuntimeException(
"Error unpublishing page: " .
json_encode($errorResponse->toArray())
);
}
}
}
// Example usage
try {
$baseUrl = 'https://api.instapage.com';
$apiKey = 'your_api_key_here';
$workspaceId = 1234;
$pageId = 5678;
$unpublisher = new PageUnpublisher($baseUrl, $apiKey);
$unpublisher->unpublishPage($pageId, $workspaceId);
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
The ID of the workspace |
pageId |
number |
The ID of the page to unpublish |
Responses
Status | Description |
---|---|
201 |
Created. The page was successfully unpublished. |
400 |
Bad Request. The page is not published. |
401 |
Unauthorized. Authentication failed. |
403 |
Forbidden. The user doesn't have the necessary permissions. |
404 |
Not Found. The specified page was not found. |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. Please slow down and retry after some time. |
500 |
Internal Server Error. An unexpected error occurred. |
Search Pages Globally
Search for pages across all accessible workspaces by ID, title, or URL.
HTTP Request
https://api.instapage.com/v1/pages/search
curl "https://api.instapage.com/v1/pages/search?page=1&id=9371&title=my%20page" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json"
async function searchPages(page, searchParams, apiKey) {
const url = new URL('https://api.instapage.com/v1/pages/search');
url.searchParams.append('page', page);
// Add optional search parameters
if (searchParams.id) url.searchParams.append('id', searchParams.id);
if (searchParams.title) url.searchParams.append('title', searchParams.title);
if (searchParams.url) url.searchParams.append('url', searchParams.url);
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
});
if (!response.ok) {
throw new Error(`Failed to search pages: ${response.statusText}`);
}
return await response.json();
}
type SearchParams = {
id?: number;
title?: string;
url?: string;
};
type SearchResult = {
id: number;
workspaceId: number;
title: string;
url: string | null;
};
type SearchResponse = {
data: SearchResult[];
meta: {
requestedPageId: number | null;
requestedPageTitle: string | null;
requestedPageUrl: string | null;
pagination: {
currentPage: number;
perPage: number;
totalItemsCount: number;
totalPagesCount: number;
nextPage: number | null;
previousPage: number | null;
};
};
};
async function searchPages(
page: number,
searchParams: SearchParams,
apiKey: string
): Promise<SearchResponse> {
const url = new URL('https://api.instapage.com/v1/pages/search');
url.searchParams.append('page', page.toString());
if (searchParams.id) url.searchParams.append('id', searchParams.id.toString());
if (searchParams.title) url.searchParams.append('title', searchParams.title);
if (searchParams.url) url.searchParams.append('url', searchParams.url);
const response = await fetch(url.toString(), {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
});
if (!response.ok) {
throw new Error(`Failed to search pages: ${response.statusText}`);
}
return await response.json();
}
<?php
declare(strict_types=1);
namespace InstapagePageSearch;
readonly class SearchParams
{
public function __construct(
public ?int $id = null,
public ?string $title = null,
public ?string $url = null
) {}
public function toQueryString(): string
{
$params = [];
if ($this->id !== null) {
$params['id'] = $this->id;
}
if ($this->title !== null) {
$params['title'] = $this->title;
}
if ($this->url !== null) {
$params['url'] = $this->url;
}
return http_build_query($params);
}
}
readonly class SearchResult
{
public function __construct(
public int $id,
public int $workspaceId,
public string $title,
public ?string $url
) {}
public static function fromArray(array $data): self
{
return new self(
$data['id'],
$data['workspaceId'],
$data['title'],
$data['url'] ?? null
);
}
}
class PageSearcher
{
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
public function searchPages(int $page, SearchParams $params): array
{
$queryString = $params->toQueryString();
$url = sprintf(
'%s/v1/pages/search?page=%d&%s',
$this->baseUrl,
$page,
$queryString
);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
]
]);
$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new \RuntimeException('CURL Error: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode !== 200) {
throw new \RuntimeException("Search request failed: HTTP $httpCode");
}
$responseData = json_decode($responseBody, true);
return array_map(
fn($result) => SearchResult::fromArray($result),
$responseData['data']
);
}
}
// Example usage
try {
$searcher = new PageSearcher(
'https://api.instapage.com',
'your_api_key_here'
);
$params = new SearchParams(
title: 'Landing Page'
);
$results = $searcher->searchPages(1, $params);
foreach ($results as $result) {
echo sprintf(
"Found page: %s (ID: %d, Workspace: %d)\n",
$result->title,
$result->id,
$result->workspaceId
);
}
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
The above request returns JSON structured like this:
{
"data": [
{
"id": 9371,
"workspaceId": 7068,
"title": "My Landing Page",
"url": "landing.example.com"
},
{
"id": 9372,
"workspaceId": 7068,
"title": "Product Launch Page",
"url": null
}
],
"meta": {
"requestedPageId": null,
"requestedPageTitle": "page",
"requestedPageUrl": null,
"pagination": {
"currentPage": 1,
"perPage": 100,
"totalItemsCount": 2,
"totalPagesCount": 1,
"nextPage": null,
"previousPage": null
}
}
}
Query Parameters
Parameter | Type | Default | Description |
---|---|---|---|
page |
number |
1 |
Page number for pagination. |
id |
number |
null |
Filter results by page ID. |
title |
string |
null |
Filter results by page title (case-insensitive). |
url |
string |
null |
Filter results by published URL. |
Response JSON structure
JSON Path | Type | Description |
---|---|---|
data[].id |
number |
Unique identifier of the page. |
data[].workspaceId |
number |
ID of the workspace containing the page. |
data[].title |
string |
Title of the page. |
data[].url |
string? |
Published URL of the page (null if unpublished). |
meta.requestedPageId |
number? |
The page ID used in the search request. |
meta.requestedPageTitle |
string? |
The page title used in the search request. |
meta.requestedPageUrl |
string? |
The URL used in the search request. |
meta.pagination.currentPage |
number |
Current page number. |
meta.pagination.perPage |
number |
Number of results per page. |
meta.pagination.totalItemsCount |
number |
Total number of results found. |
meta.pagination.totalPagesCount |
number |
Total number of pages available. |
meta.pagination.nextPage |
number? |
Next page number, null if no more pages. |
meta.pagination.previousPage |
number? |
Previous page number, null if on first page. |
Response status codes
Status | Description |
---|---|
200 |
The request was processed successfully. |
400 |
Bad request. Invalid parameters provided. |
401 |
Unauthorized. Authentication failed or missing credentials. |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. Please slow down and retry after some time. |
500 |
Internal Server Error. An unexpected condition prevented the request from being fulfilled. |
Update Page
Update a page's properties such as its group assignment.
HTTP Request
https://api.instapage.com/v1/workspaces/{workspaceId}/pages/{pageId}
curl -X PATCH \
"https://api.instapage.com/v1/workspaces/{workspaceId}/pages/{pageId}" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"groupId": 7340
}'
async function updatePage(workspaceId, pageId, updateData, apiKey) {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/pages/${pageId}`;
const response = await fetch(url, {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(updateData)
});
if (response.status !== 201) {
const errorData = await response.json();
throw new Error(`Failed to update page: ${errorData.details}`);
}
return true;
}
// Example usage
const updateData = { groupId: 7340 };
try {
await updatePage(workspaceId, pageId, updateData, apiKey);
console.log('Page updated successfully');
} catch (error) {
console.error('Error updating page:', error);
}
interface PageUpdateRequest {
groupId?: number | null;
}
interface ErrorResponse {
title: string;
details: string;
meta: {
pageId: number;
workspaceId: number;
userId: number;
groupId?: number;
};
}
async function updatePage(
workspaceId: number,
pageId: number,
updateData: PageUpdateRequest,
apiKey: string
): Promise<boolean> {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/pages/${pageId}`;
const response = await fetch(url, {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(updateData)
});
if (response.status !== 201) {
const errorData: ErrorResponse = await response.json();
throw new Error(`Failed to update page: ${errorData.details}`);
}
return true;
}
<?php
declare(strict_types=1);
namespace InstapagePageUpdater;
readonly class ErrorResponse {
public function __construct(
public string $title,
public string $details,
public int $pageId,
public int $workspaceId,
public int $userId,
public ?int $groupId = null
) {}
public static function fromArray(array $data): self {
return new self(
$data['title'],
$data['details'],
$data['meta']['pageId'],
$data['meta']['workspaceId'],
$data['meta']['userId'],
$data['meta']['groupId'] ?? null
);
}
}
class PageUpdater {
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
public function updatePage(
int $workspaceId,
int $pageId,
?int $groupId = null
): bool {
$url = sprintf(
'%s/v1/workspaces/%d/pages/%d',
$this->baseUrl,
$workspaceId,
$pageId
);
$payload = json_encode(['groupId' => $groupId]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
]
]);
$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new \RuntimeException('CURL Error: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode === 201) {
return true;
}
$errorData = json_decode($responseBody, true);
$error = ErrorResponse::fromArray($errorData);
throw new \RuntimeException("Failed to update page: {$error->details}");
}
}
// Example usage
try {
$updater = new PageUpdater(
'https://api.instapage.com',
'your_api_key_here'
);
// Move page to a group
$updater->updatePage(7068, 9399, 7340);
// Remove page from any group by passing null
$updater->updatePage(7068, 9399, null);
echo "Page updated successfully\n";
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage() . "\n";
}
Example error response:
{
"title": "UpdateGroupException",
"details": "The Page is already in that Group",
"meta": {
"pageId": 9399,
"workspaceId": 7068,
"userId": 4373,
"groupId": 7340
}
}
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
The ID of the workspace containing the page |
pageId |
number |
The ID of the page to update |
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Request Body
Parameter | Type | Description |
---|---|---|
groupId |
number? |
The ID of the group to move the page to, or null to remove from all groups |
Response Status Codes
Status | Description |
---|---|
201 |
Created. The page was successfully updated. |
400 |
Bad Request. Invalid input parameters or the page is already in the specified group. |
401 |
Unauthorized. Authentication failed or missing credentials. |
403 |
Forbidden. The user doesn't have necessary permissions to update the page. |
404 |
Not Found. The specified page, workspace, or group could not be found. |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. Please slow down and retry after some time. |
500 |
Internal Server Error. An unexpected condition prevented the request from being fulfilled. |
Related articles
- Publishing options
- Creating a new page
- How do I publish my page to my own domain
- Publishing Your Page to Drupal
- Publishing through WordPress.org
- Troubleshooting WordPress or Drupal publishing
Personalizations
Get All Personalizations
Retrieve all personalizations for a specific page.
HTTP Request
https://api.instapage.com/v1/workspaces/{workspaceId}/pages/{pageId}/personalizations
curl "https://api.instapage.com/v1/workspaces/{workspaceId}/pages/{pageId}/personalizations" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json"
Example of body response from request
{
"data": [
{
"pageId": 9402,
"personalizationId": "108b2b17-478d-4f8f-9059-003d44a89b39",
"title": "example page",
"publishStatus": "published",
"createdAt": 1696332561,
"url": "ip.example.com?108b2b17=003d44a89b39&id-param=example-page",
"publishMethod": "customDomain",
"isDefaultPersonalization": false,
"isScheduled": false
},
{
"pageId": 9399,
"personalizationId": "18d22df0-7198-48ab-8301-9c110f71d667",
"title": "default example page",
"publishStatus": "published",
"createdAt": 1692168365,
"url": "ip.example.com",
"publishMethod": "customDomain",
"isDefaultPersonalization": true,
"isScheduled": false
}
],
"meta": {
"pagination": {
"currentPage": 1,
"perPage": 100,
"totalItemsCount": 2,
"totalPagesCount": 1,
"nextPage": null,
"previousPage": null
}
}
}
async function fetchPersonalizations(workspaceId, pageId, apiKey) {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/pages/${pageId}/personalizations`;
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
});
if (!response.ok) {
throw new Error(`Failed to fetch personalizations: ${response.statusText}`);
}
const data = await response.json();
return data;
}
type Personalization = {
pageId: number;
personalizationId: string;
title: string;
publishStatus: 'published' | 'unpublished';
createdAt: number;
url: string | null;
publishMethod: string;
isDefaultPersonalization: boolean;
};
type PaginationMeta = {
current_page: number;
per_page: number;
total_items_count: number;
total_pages_count: number;
next_page: number | null;
previous_page: number | null;
};
type PersonalizationsResponse = {
data: Personalization[];
meta: {
pagination: PaginationMeta;
};
};
async function fetchPersonalizations(
workspaceId: number,
pageId: number,
apiKey: string
): Promise<PersonalizationsResponse> {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/pages/${pageId}/personalizations`;
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
});
if (!response.ok) {
throw new Error(`Failed to fetch personalizations: ${response.statusText}`);
}
const data: PersonalizationsResponse = await response.json();
return data;
}
<?php
declare(strict_types=1);
namespace InstapagePersonalizationsFetcher;
enum PublishStatus: string
{
case Published = 'published';
case Unpublished = 'unpublished';
}
readonly class Personalization
{
public function __construct(
public int $pageId,
public string $personalizationId,
public string $title,
public PublishStatus $publishStatus,
public int $createdAt,
public ?string $url,
public string $publishMethod,
public bool $isDefaultPersonalization
) {}
public function toArray(): array
{
return [
'pageId' => $this->pageId,
'personalizationId' => $this->personalizationId,
'title' => $this->title,
'publishStatus' => $this->publishStatus->value,
'createdAt' => $this->createdAt,
'url' => $this->url,
'publishMethod' => $this->publishMethod,
'isDefaultPersonalization' => $this->isDefaultPersonalization
];
}
public static function fromArray(array $data): self
{
return new self(
$data['pageId'],
$data['personalizationId'],
$data['title'],
PublishStatus::from($data['publishStatus']),
$data['createdAt'],
$data['url'] ?? null,
$data['publishMethod'],
$data['isDefaultPersonalization']
);
}
}
readonly class PaginationMeta
{
public function __construct(
public int $currentPage,
public int $perPage,
public int $totalItemsCount,
public int $totalPagesCount,
public ?int $nextPage,
public ?int $previousPage
) {}
public function toArray(): array
{
return [
'current_page' => $this->currentPage,
'per_page' => $this->perPage,
'total_items_count' => $this->totalItemsCount,
'total_pages_count' => $this->totalPagesCount,
'next_page' => $this->nextPage,
'previous_page' => $this->previousPage
];
}
public static function fromArray(array $data): self
{
return new self(
$data['current_page'],
$data['per_page'],
$data['total_items_count'],
$data['total_pages_count'],
$data['next_page'],
$data['previous_page']
);
}
}
class PersonalizationsResponse
{
/**
* @param Personalization[] $data
*/
public function __construct(
private array $data,
private PaginationMeta $paginationMeta
) {}
public function getData(): array
{
return $this->data;
}
public function getMeta(): PaginationMeta
{
return $this->paginationMeta;
}
}
class PersonalizationsFetcher
{
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
public function fetchPersonalizations(
int $workspaceId,
int $pageId
): PersonalizationsResponse {
$url = sprintf(
'%s/v1/workspaces/%d/pages/%d/personalizations',
$this->baseUrl,
$workspaceId,
$pageId
);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
]
]);
$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new \RuntimeException('CURL Error: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode !== 200) {
throw new \RuntimeException("Failed to fetch personalizations: HTTP $httpCode");
}
$responseData = json_decode($responseBody, true);
return new PersonalizationsResponse(
array_map(
fn($personalizationData) => Personalization::fromArray($personalizationData),
$responseData['data']
),
PaginationMeta::fromArray($responseData['meta']['pagination'])
);
}
}
// Example usage
try {
$baseUrl = 'https://api.instapage.com';
$apiKey = 'your_api_key_here';
$workspaceId = 1234;
$pageId = 5678;
$fetcher = new PersonalizationsFetcher($baseUrl, $apiKey);
$personalizationsResponse = $fetcher->fetchPersonalizations($workspaceId, $pageId);
// Process personalizations
foreach ($personalizationsResponse->getData() as $personalization) {
// Do something with each personalization
print_r($personalization->toArray());
}
// Access pagination metadata
$paginationMeta = $personalizationsResponse->getMeta();
print_r($paginationMeta->toArray());
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
The ID of the workspace |
pageId |
number |
The ID of the page to retrieve personalizations for |
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Response JSON structure
JSON Path | Type | Description |
---|---|---|
data[].pageId |
number |
Unique identifier of the page. |
data[].personalizationId |
string |
Unique identifier for the personalization experience. |
data[].title |
string |
Title of the page. |
data[].publishStatus |
string |
Current publish status of the page (e.g., published ). |
data[].createdAt |
number |
Creation timestamp of the page (UNIX format). |
data[].url |
string |
URL where the page is published. |
data[].publishMethod |
string |
Method used for publishing the page (e.g., customDomain ). |
data[].isDefaultPersonalization |
boolean |
Indicates whether this is the default personalization experience. |
data[].isScheduled |
boolean |
Indicates whether the page is scheduled for publication. |
meta.pagination.currentPage |
number |
Number of the current page of results. |
meta.pagination.perPage |
number |
Number of items per page. |
meta.pagination.totalItemsCount |
number |
Total number of items. |
meta.pagination.totalPagesCount |
number |
Total number of pages. |
meta.pagination.nextPage |
number? |
Number of the next page, or null if there is no next page. |
meta.pagination.previousPage |
number? |
Number of the previous page, or null if there is no previous page. |
This endpoint retrieves all personalizations for the specified page. The response includes detailed information about each personalization, such as its ID, title, publish status, and URL. The results are paginated, and you can use the pagination metadata to navigate through the pages of results if necessary.
Response status codes
Status | Description |
---|---|
200 |
The request was processed successfully. |
400 |
Bad request. Validation error — review input parameters. |
401 |
Unauthorized. Authentication failed or missing credentials. |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. Please slow down and retry after some time. |
500 |
Internal Server Error. An unexpected condition prevented the request from being fulfilled. |
Analytics
Get statistical data
Retrieve statistical data related to pages and experiences in a bulk.
HTTP Request
https://api.instapage.com/v1/workspaces/:workspaceId/analytics
curl "https://api.instapage.com/v1/workspaces/$workspaceId/analytics" \
-H "Authorization: Bearer $API_KEY"
-H 'cache-control: no-cache' \
-H 'content-type: application/json' \
--data '{
"visited": 0,
"pages": [
20476657,
10716956,
10842011
],
"timeframe": {
"start": 0,
"end": 1726150655561
},
"grouping": ["pageId"],
"interval": "yearly"
}'
async function fetchAnalytics(workspaceId, options, apiKey) {
const url =`https://api.instapage.com/v1/workspaces/${workspaceId}/analytics`;
const response = await fetch(url, {
method: 'POST',
body: JSON.stringify(options),
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
});
if (!response.ok) {
throw new Error(`Failed to fetch workspaces: ${response.statusText}`);
}
const data = await response.json();
return data;
}
type AnalyticsFetchOptions = {
pages: number[];
interval: 'daily' | 'hourly' | 'monthly' | 'yearly';
grouping?: ('pageId' | 'variationId')[];
timeframe?: {
start: number;
end: number;
};
visited?: number;
device?: 'any' | 'desktop' | 'mobile';
};
type AnalyticsFetchResponse = {
data: {
key: {
pageId?: number;
date?: number;
variationId?: number;
};
visit: number;
conversion: number;
leads: number;
}[];
};
async function fetchAnalytics(workspaceId: number, options: AnalyticsFetchOptions, apiKey: string) {
const url =`https://api.instapage.com/v1/workspaces/${workspaceId}/analytics`;
const response = await fetch(url, {
method: 'POST',
body: JSON.stringify(options),
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
});
if (!response.ok) {
throw new Error(`Failed to fetch workspaces: ${response.statusText}`);
}
const data = await response.json();
return data as AnalyticsFetchResponse;
}
<?php
declare(strict_types=1);
namespace InstapageAnalytics;
class AnalyticsFetchOptions
{
public function __construct(
public readonly array $pages,
public readonly string $interval,
public readonly ?array $grouping = null,
public readonly ?array $timeframe = null,
public readonly ?int $visited = null,
public readonly ?string $device = null
) {}
public function toArray(): array
{
$options = [
'pages' => $this->pages,
'interval' => $this->interval,
];
if ($this->grouping !== null) {
$options['grouping'] = $this->grouping;
}
if ($this->timeframe !== null) {
$options['timeframe'] = $this->timeframe;
}
if ($this->visited !== null) {
$options['visited'] = $this->visited;
}
if ($this->device !== null) {
$options['device'] = $this->device;
}
return $options;
}
}
class AnalyticsFetchResponse
{
public function __construct(
public readonly array $data
) {}
public static function fromArray(array $responseData): self
{
return new self($responseData);
}
}
class InstapageAnalyticsFetcher
{
public function __construct(
private readonly string $baseUrl,
private readonly string $apiKey
) {}
public function fetchAnalytics(
int $workspaceId,
AnalyticsFetchOptions $options
): AnalyticsFetchResponse {
$url = sprintf(
'%s/v1/workspaces/%d/analytics',
$this->baseUrl,
$workspaceId
);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . $this->apiKey
],
CURLOPT_POSTFIELDS => json_encode($options->toArray(), JSON_THROW_ON_ERROR)
]);
$responseBody = curl_exec($ch);
if ($responseBody === false) {
throw new \RuntimeException('Curl request failed: ' . curl_error($ch));
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode < 200 || $httpCode >= 300) {
throw new \RuntimeException(sprintf(
'API request failed with status code %d: %s',
$httpCode,
$responseBody
));
}
$responseData = json_decode($responseBody, true, 512, JSON_THROW_ON_ERROR);
return AnalyticsFetchResponse::fromArray($responseData);
}
}
// Example usage
try {
$baseUrl = 'https://api.instapage.com';
$apiKey = 'your_api_key_here';
$fetcher = new InstapageAnalyticsFetcher($apiKey, $baseUrl);
$options = new AnalyticsFetchOptions(
pages: [1, 2, 3],
interval: 'daily',
grouping: ['date'],
timeframe: [
'start' => time() - 30 * 24 * 60 * 60, // 30 days ago
'end' => time()
]
);
$response = $fetcher->fetchAnalytics(123, $options);
print_r($response->data);
} catch (\JsonException $e) {
// Handle JSON encoding/decoding errors
echo "JSON Error: " . $e->getMessage();
} catch (\RuntimeException $e) {
// Handle API request errors
echo "API Request Error: " . $e->getMessage();
}
The above request returns JSON structured like this:
{
"data": [
{
"key": {
"pageId": 10842011,
"date": 1727275000
},
"visit": 42,
"conversion": 5,
"leads": 5
},
{
"key": {
"pageId": 10716956,
"date": 1727276000
},
"visit": 1,
"conversion": 1,
"leads": 1
},
{
"key": {
"pageId": 10716956,
"date": 1727277000
},
"visit": 1,
"conversion": 0,
"leads": 0
},
{
"key": {
"pageId": 10842011,
"date": 1727278000
},
"visit": 181,
"conversion": 0,
"leads": 0
}
]
}
The key depends on grouping used and will behave differently for different grouping requests.
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
Specifies which workspace to use. |
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Body Parameters
Content-type should be of type application/json
.
JSON Path | Type | Default | Description |
---|---|---|---|
pages |
number[] |
null |
Specifies the pages for which the API should return statistical data. Note: You can provide up to 100 page IDs at once. |
interval |
string |
monthly |
Specifies the time interval for aggregating data. Options include daily , hourly , monthly , or yearly . |
device |
string |
any |
Determines the device type filter. Can be set to any , desktop , or mobile visits. |
timeframe.start |
number |
0 |
Defines the starting timestamp (in seconds) for fetching data. |
timeframe.end |
number |
'current time' |
Defines the ending timestamp (in seconds) for the data retrieval period. |
traffic |
string |
blended |
Specifies the type of traffic to include: blended , organic , or paid . |
grouping |
string[] |
[] |
Defines additional grouping for the data. Options include pageId and variationId . |
visited |
number |
null |
Filters data based on unique actions. A value of 1 includes only returning visitors; a value of 0 includes only unique actions. |
Response JSON structure
JSON Path | Type | Description |
---|---|---|
data[].key |
Object |
Object containing unique identifiers. Structure varies based on the request. |
data[].key.pageId |
number (optional) |
Page ID, part of the key, optional - reflects grouping of request. |
data[].key.date |
number (optional) |
Date in UNIX timestamp (seconds), part of the key, optional - reflects grouping of request. |
data[].key.variationId |
number (optional) |
Variation ID, part of the key, optional - reflects grouping of request. |
data[].visit |
number |
Total number of visits associated with the key. |
data[].conversion |
number |
Total number of conversions associated with the key. |
data[].leads |
number |
Total number of leads associated with the key. |
Response status codes
Status | Description |
---|---|
200 |
The request was processed successfully. |
400 |
Bad request. Validation error — review input parameters. |
401 |
Unauthorized. Authentication failed or missing credentials. |
403 |
Forbidden. The user does not have the necessary permissions. |
404 |
Not Found. The requested resource could not be located. |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. Please slow down and retry after some time. |
500 |
Internal Server Error. An unexpected condition prevented the request from being fulfilled. |
Related articles
Form Submissions
Form submissions refer to the data collected when a user successfully fills out and submits a form on your landing page.
Retrieve Form Submissions
Fetch form submission data from specified pages, with optional filtering by time range.
HTTP Request
https://api.instapage.com/v1/workspaces/{workspaceId}/submissions
curl "https://api.instapage.com/v1/workspaces/7068/submissions" \
-H "Authorization: Bearer API_KEY" \
-H "Content-Type: application/json" \
--data '{"pages": [9371, 9370], "timeframe": {
"start": 1717259412,
"end": 1727251907
}
}'
async function fetchSubmissions(workspaceId, pages, timerange, nextPageToken, apiKey) {
const url = new URL(`https://api.instapage.com/v1/workspaces/${workspaceId}/submissions`);
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
method: 'POST',
body: JSON.stringify({ pages, ...timerange ? { timerange } : null, nextPageToken })
});
if (!response.ok) {
throw new Error(`Failed to fetch pages: ${response.statusText}`);
}
const data = await response.json();
return data;
}
type Filters = {
pages: number[];
timerange?: {
start: number;
end: number;
};
nextPageToken?: string;
};
type Submission = {
id: string;
pageId: number;
variationName: string;
variationCustomName: string;
createdAt: number;
fields: Record<string, string>;
}
type SubmissionsResponse = {
data: Submission[];
meta: {
nextPageToken: string | null;
limit: number;
};
};
async function fetchFormSubmissions(
apiKey: string,
workspaceId: number;
filters: Options;
): Promise<SubmissionsResponse> {
const url = new URL(`https://api.instapage.com/v1/workspaces/${workspaceId}/pages`);
const json = JSON.stringify(filters);
const response = await fetch(url.toString(), {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
method: 'POST',
body: json,
});
if (!response.ok) {
throw new Error(`Failed to fetch submissions: ${response.statusText}`);
}
const data: SubmissionsResponse = await response.json();
return data;
}
<?php
declare(strict_types=1);
namespace InstapageSubmissionsFetcher;
readonly class ApiConfig
{
public function __construct(
public string $baseUrl,
public string $apiKey,
public int $workspaceId
) {}
}
readonly class Filters
{
public function __construct(
private array $pages,
private ?array $timerange = null,
private ?string $nextPageToken = null
) {}
public function toArray(): array
{
$filters = ['pages' => $this->pages];
if ($this->timerange !== null) {
$filters['timerange'] = $this->timerange;
}
if ($this->nextPageToken !== null) {
$filters['nextPageToken'] = $this->nextPageToken;
}
return $filters;
}
}
class Submission
{
public function __construct(
private string $id,
private int $pageId,
private string $variationName,
private string $variationCustomName,
private int $createdAt,
private array $fields
) {}
public function toArray(): array
{
return [
'id' => $this->id,
'pageId' => $this->pageId,
'variationName' => $this->variationName,
'variationCustomName' => $this->variationCustomName,
'createdAt' => $this->createdAt,
'fields' => $this->fields
];
}
}
class SubmissionsResponse
{
public function __construct(
private array $data,
private ?string $nextPageToken,
private int $limit
) {}
public function getData(): array
{
return $this->data;
}
public function getNextPageToken(): ?string
{
return $this->nextPageToken;
}
public function getLimit(): int
{
return $this->limit;
}
}
class SubmissionsFetcher
{
public function __construct(private ApiConfig $apiConfig) {}
public function fetchFormSubmissions(Filters $filters): SubmissionsResponse
{
$url = sprintf(
'%s/v1/workspaces/%d/pages',
$this->apiConfig->baseUrl,
$this->apiConfig->workspaceId
);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiConfig->apiKey,
'Content-Type: application/json'
],
CURLOPT_POSTFIELDS => json_encode($filters->toArray())
]);
$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new \RuntimeException('CURL Error: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode !== 200) {
throw new \RuntimeException("API request failed with status code: $httpCode");
}
$responseData = json_decode($responseBody, true);
return new SubmissionsResponse(
array_map(fn($submissionData) => new Submission(
$submissionData['id'],
$submissionData['pageId'],
$submissionData['variationName'],
$submissionData['variationCustomName'],
$submissionData['createdAt'],
$submissionData['fields']
), $responseData['data']),
$responseData['meta']['nextPageToken'] ?? null,
$responseData['meta']['limit']
);
}
}
// Example usage
try {
$baseUrl = 'https://api.instapage.com';
$apiKey = 'your_api_key_here';
$workspaceId = 1234;
$apiConfig = new ApiConfig($baseUrl, $apiKey, $workspaceId);
$fetcher = new SubmissionsFetcher($apiConfig);
$filters = new Filters(
pages: [1, 2, 3],
timerange: ['start' => time() - 86400, 'end' => time()],
nextPageToken: null
);
$submissionsResponse = $fetcher->fetchFormSubmissions($filters);
// Process submissions
foreach ($submissionsResponse->getData() as $submission) {
// Do something with each submission
print_r($submission->toArray());
}
// Check for next page
$nextPageToken = $submissionsResponse->getNextPageToken();
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
The above request returns JSON structured like this:
{
"meta": {
"limit": 100,
"nextPageToken": "6672e0f595fd7364f4c8973f"
},
"data": {
"submissions": [
{
"id": "66797c440bf4f63b67fd1054",
"pageId": 9371,
"variationName": "A",
"variationCustomName": "A",
"createdAt": 1727261810,
"fields": {
"form-field-1": "value-of-form",
}
},
{
"id": "66797c3e0bf4f63b67fd1053",
"pageId": 9371,
"variationName": "A",
"variationCustomName": "A",
"createdAt": 1727261811,
"fields": {
"Email": "email@example.com",
}
},
{
"id": "66795e1323047c1af4cca224",
"pageId": 9370,
"variationName": "A",
"variationCustomName": "A",
"createdAt": 0,
"fields": {
"Name": "xdxd",
}
},
{
"id": "6672e0f595fd7364f4c8973f",
"pageId": 9370,
"variationName": "A",
"variationCustomName": "A",
"createdAt": 1727261813,
"fields": {
"Email": "customer1@example.com",
}
}
]
}
}
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
The ID of the workspace to retrieve submissions for |
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Body Parameters
JSON Path | Type | Default | Description |
---|---|---|---|
pages |
number[] |
[] |
A list of page IDs to filter form submissions. Note: You can provide up to 100 page IDs at once. |
timeframe.start |
number |
null |
Defines the start of the time range for filtering leads, specified in seconds. |
timeframe.end |
number |
null |
Defines the end of the time range for filtering leads, specified in seconds. |
nextPageToken |
string |
null |
Token for pagination; retrieves the next set of form submissions when provided. |
Response JSON structure
JSON Path | Type | Description |
---|---|---|
data[].id |
string |
Unique identifier for the submission. |
data[].pageId |
number |
ID of the page related to the submission. |
data[].variationName |
string |
Name of the variation used for the submission. |
data[].variationCustomName |
string |
Custom name of the variation used for the submission. |
data[].createdAt |
number |
Timestamp (in UNIX seconds) of when the submission was created. |
data[].fields |
Object |
Key-value pairs representing submission fields. |
meta.nextPageToken |
string? |
Token for fetching the next page of submissions, or null if there is no next page. |
meta.limit |
number |
Maximum number of submissions returned per page. |
This endpoint allows retrieval of form submissions from multiple pages in a single request. The data limit is capped at 100 submissions per page. To retrieve additional pages, use the nextPageToken
.
When the number of retrieved submissions is less than the limit, an empty array is returned, or nextPageToken
is null
, it indicates that no further submissions are available from the endpoint.
Response status codes
Status | Description |
---|---|
200 |
The request was processed successfully. |
400 |
Bad request. Some filter parameters are most likely not valid. |
401 |
Unauthorized. Authentication failed or missing credentials. |
403 |
Forbidden. The user does not have the necessary permissions. |
404 |
Not Found. The requested resource could not be located. |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. Please slow down and retry after some time. |
500 |
Internal Server Error. An unexpected condition prevented the request from being fulfilled. |
Delete Form Submission
Permanently delete form submissions from specified pages. This action is irreversible, so use it with caution.
HTTP Request
https://api.instapage.com/v1/workspaces/{workspaceId}/submissions
curl -X DELETE "https://api.instapage.com/v1/workspaces/7068/submissions" \
-H "Authorization: Bearer API_KEY" \
-H "Content-Type: application/json" \
--data '{"submissions": ["submission_id_1", "submission_id_2"]}'
async function deleteSubmissions(workspaceId, submissionIds, apiKey) {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/submissions`;
const response = await fetch(url, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ submissions: submissionIds })
});
if (!response.ok) {
throw new Error(`Failed to delete submissions: ${response.statusText}`);
}
if (response.status === 204) {
console.log('Submissions deleted successfully.');
}
}
// Example usage
const workspaceId = 7068;
const submissionIds = ["submission_id_1", "submission_id_2"];
const apiKey = "your_api_key_here";
deleteSubmissions(workspaceId, submissionIds, apiKey)
.catch(error => console.error(error));
type DeleteSubmissionsRequest = {
submissions: string[];
};
async function deleteFormSubmissions(
apiKey: string,
workspaceId: number,
submissionIds: string[]
): Promise<void> {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/submissions`;
const body: DeleteSubmissionsRequest = { submissions: submissionIds };
const response = await fetch(url, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
});
if (!response.ok) {
throw new Error(`Failed to delete submissions: ${response.statusText}`);
}
if (response.status === 204) {
console.log('Submissions deleted successfully.');
}
}
// Example usage
const apiKey = "your_api_key_here";
const workspaceId = 7068;
const submissionIds = ["submission_id_1", "submission_id_2"];
deleteFormSubmissions(apiKey, workspaceId, submissionIds)
.catch(error => console.error(error));
<?php
declare(strict_types=1);
namespace InstapageSubmissionsDeleter;
readonly class ApiConfig
{
public function __construct(
public string $baseUrl,
public string $apiKey,
public int $workspaceId
) {}
}
class SubmissionsDeleter
{
public function __construct(private ApiConfig $apiConfig) {}
public function deleteSubmissions(array $submissionIds): void
{
$url = sprintf(
'%s/v1/workspaces/%d/submissions',
$this->apiConfig->baseUrl,
$this->apiConfig->workspaceId
);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiConfig->apiKey,
'Content-Type: application/json'
],
CURLOPT_POSTFIELDS => json_encode(['submissions' => $submissionIds])
]);
$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new \RuntimeException('CURL Error: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode !== 204) {
throw new \RuntimeException("Failed to delete submissions. Status code: $httpCode");
}
echo 'Submissions deleted successfully.';
}
}
// Example usage
try {
$baseUrl = 'https://api.instapage.com';
$apiKey = 'your_api_key_here';
$workspaceId = 7068;
$apiConfig = new ApiConfig($baseUrl, $apiKey, $workspaceId);
$deleter = new SubmissionsDeleter($apiConfig);
$submissionIds = ["submission_id_1", "submission_id_2"];
$deleter->deleteSubmissions($submissionIds);
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
The ID of the workspace to retrieve submissions for |
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Body Parameters
JSON Path | Type | Default | Description |
---|---|---|---|
submissions |
string[] |
[] |
An array of submission IDs to delete. Note: You can provide between 1 and 100 submission IDs per request. |
Response status codes
Status | Description |
---|---|
204 |
The submissions were deleted successfully. |
400 |
Bad request. The input is invalid (e.g., empty or more than 100 submission IDs). |
401 |
Unauthorized. Authentication failed or missing credentials. |
403 |
Forbidden. The user does not have the necessary permissions. |
404 |
Not Found. The requested resource could not be located. |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. Please slow down and retry after some time. |
500 |
Internal Server Error. An unexpected condition prevented the request from being fulfilled. |
Related articles
Experiments
Get All Experiments
Retrieve all experiments for a specific workspace with optional filtering by status.
HTTP Request
https://api.instapage.com/v1/workspaces/{workspaceId}/experiments
curl "https://api.instapage.com/v1/workspaces/$workspaceId/experiments?status[]=DRAFT&status[]=ENDED&page=1" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json"
async function fetchExperiments(workspaceId, statuses, page, apiKey) {
const url = new URL(`https://api.instapage.com/v1/workspaces/${workspaceId}/experiments`);
if (statuses && statuses.length) {
statuses.forEach(status => url.searchParams.append('status[]', status));
}
if (page) {
url.searchParams.append('page', page);
}
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
});
if (!response.ok) {
throw new Error(`Failed to fetch experiments: ${response.statusText}`);
}
return await response.json();
}
type ExperimentType = 'Manual' | 'AI';
type ExperimentStatus = 'DRAFT' | 'RUNNING' | 'ENDED' | 'ARCHIVED';
type Experiment = {
experimentId: number;
experimentName: string;
pageId: number;
publishedPageUrl: string;
experimentType: ExperimentType;
experimentStatus: ExperimentStatus;
createdAt: number;
startedAt: number | null;
endedAt: number | null;
createdBy: number;
};
type PaginationMeta = {
currentPage: number;
perPage: number;
totalItemsCount: number;
totalPagesCount: number;
nextPage: number | null;
previousPage: number | null;
};
type ExperimentsResponse = {
data: Experiment[];
meta: {
pagination: PaginationMeta;
};
};
async function fetchExperiments(
workspaceId: number,
apiKey: string,
statuses?: ExperimentStatus[],
page?: number,
): Promise<ExperimentsResponse> {
const url = new URL(`https://api.instapage.com/v1/workspaces/${workspaceId}/experiments`);
if (statuses?.length) {
statuses.forEach(status => url.searchParams.append('status[]', status));
}
if (page) {
url.searchParams.append('page', page);
}
const response = await fetch(url.toString(), {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
});
if (!response.ok) {
throw new Error(`Failed to fetch experiments: ${response.statusText}`);
}
return await response.json();
}
<?php
declare(strict_types=1);
namespace InstapageExperimentsFetcher;
enum ExperimentType: string
{
case MANUAL = 'Manual';
case AI = 'AI';
}
enum ExperimentStatus: string
{
case DRAFT = 'DRAFT';
case RUNNING = 'RUNNING';
case ENDED = 'ENDED';
case ARCHIVED = 'ARCHIVED';
}
readonly class Experiment
{
public function __construct(
public int $experimentId,
public string $name,
public int $pageId,
public string $publishedPageUrl,
public ExperimentType $type,
public ExperimentStatus $status,
public int $createdAt,
public int $createdBy,
public ?int $startedAt,
public ?int $endedAt
) {}
public static function fromArray(array $data): self
{
return new self(
$data['experimentId'],
$data['experimentName'],
$data['pageId'],
$data['publishedPageUrl'],
ExperimentType::from($data['experimentType']),
ExperimentStatus::from($data['experimentStatus']),
$data['createdAt'],
$data['createdBy'],
$data['startedAt'],
$data['endedAt']
);
}
}
class ExperimentsFetcher
{
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
/**
* @param ExperimentStatus[] $statuses
*/
public function fetchExperiments(
int $workspaceId,
array $statuses = [],
int $page = 1
): array {
$url = sprintf(
'%s/v1/workspaces/%d/experiments',
$this->baseUrl,
$workspaceId
);
$queryParams = ['page' => $page];
if (!empty($statuses)) {
$queryParams['status'] = array_map(fn($status) => $status->value, $statuses);
}
$url .= '?' . http_build_query($queryParams, '', '&', PHP_QUERY_RFC3986);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
]
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new \RuntimeException("API request failed with status $httpCode");
}
$data = json_decode($response, true);
return array_map(fn($item) => Experiment::fromArray($item), $data['data']);
}
}
The above request returns JSON structured like this:
{
"data": [
{
"experimentId": 8,
"experimentName": "example-experiment-ab",
"pageId": 9399,
"publishedPageUrl": "example-page-ab.pagedemo.co",
"experimentType": "Manual",
"experimentStatus": "ENDED",
"createdAt": 1695820734,
"createdBy": 1234,
"startedAt": 1696346707,
"endedAt": 1728330460
},
{
"experimentId": 11,
"experimentName": "example-experiment-ai",
"pageId": 9389,
"publishedPageUrl": "example-page-ai.pagedemo.co",
"experimentType": "AI",
"experimentStatus": "DRAFT",
"createdAt": 1695827954,
"createdBy": 1234,
"startedAt": null,
"endedAt": null
}
],
"meta": {
"pagination": {
"currentPage": 1,
"perPage": 100,
"totalItemsCount": 2,
"totalPagesCount": 1,
"nextPage": null,
"previousPage": null
}
}
}
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
The ID of the workspace to retrieve experiments for |
Query Parameters
Parameter | Type | Default | Description |
---|---|---|---|
status[] |
string[] |
[] |
Filter experiments by status. Can include multiple values: DRAFT , RUNNING , ENDED , or ARCHIVED |
page |
number |
1 |
Page number for pagination |
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Response JSON Structure
JSON Path | Type | Description |
---|---|---|
data[].experimentId |
number |
Unique identifier for the experiment |
data[].experimentName |
string |
Name of the experiment |
data[].pageId |
number |
ID of the page associated with the experiment |
data[].publishedPageUrl |
string |
URL where the experiment page is published |
data[].experimentType |
string |
Type of experiment: Manual or AI |
data[].experimentStatus |
string |
Current status: DRAFT , RUNNING , ENDED , or ARCHIVED |
data[].createdAt |
number |
Creation timestamp (UNIX format) |
data[].createdBy |
number |
user ID of the experiment creator |
data[].startedAt |
number? |
Start timestamp (UNIX format), null if not started |
data[].endedAt |
number? |
End timestamp (UNIX format), null if not ended |
meta.pagination.currentPage |
number |
Current page number |
meta.pagination.perPage |
number |
Number of items per page |
meta.pagination.totalItemsCount |
number |
Total number of items |
meta.pagination.totalPagesCount |
number |
Total number of pages |
meta.pagination.nextPage |
number? |
Next page number, null if no next page |
meta.pagination.previousPage |
number? |
Previous page number, null if no previous page |
Response Status Codes
Status | Description |
---|---|
200 |
The request was processed successfully |
400 |
Bad request - Invalid status value or pagination parameters |
401 |
Unauthorized - Authentication failed or missing credentials |
404 |
Not Found - The requested workspace was not found |
429 |
Too Many Requests - The server is rejecting requests due to excessive rate of requests |
500 |
Internal Server Error - An unexpected condition prevented the request from being fulfilled |
Related Articles
- AI Experiments
- Settings - AI Experiments
- How do I run an experiment? (A/B testing)
- How can I keep only one variation live?
- What is a control variation?
Groups
Get All Groups
Retrieve all groups (folders) for a specific workspace.
HTTP Request
https://api.instapage.com/v1/workspaces/{workspaceId}/groups
curl "https://api.instapage.com/v1/workspaces/{workspaceId}/groups" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json"
async function fetchGroups(workspaceId, apiKey) {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/groups`;
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
});
if (!response.ok) {
throw new Error(`Failed to fetch groups: ${response.statusText}`);
}
return await response.json();
}
type Group = {
id: number;
name: string;
pages: number[];
};
type PaginationMeta = {
currentPage: number;
perPage: number;
totalItemsCount: number;
totalPagesCount: number;
nextPage: number | null;
previousPage: number | null;
};
type GroupsResponse = {
data: Group[];
meta: {
workspaceId: number;
pagination: PaginationMeta;
};
};
async function fetchGroups(
workspaceId: number,
apiKey: string
): Promise<GroupsResponse> {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/groups`;
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
});
if (!response.ok) {
throw new Error(`Failed to fetch groups: ${response.statusText}`);
}
return await response.json();
}
<?php
declare(strict_types=1);
namespace InstapageGroupsFetcher;
readonly class Group
{
public function __construct(
public int $id,
public string $name,
public array $pages
) {}
public static function fromArray(array $data): self
{
return new self(
$data['id'],
$data['name'],
$data['pages'] ?? []
);
}
public function toArray(): array
{
return [
'id' => $this->id,
'name' => $this->name,
'pages' => $this->pages
];
}
}
readonly class PaginationMeta
{
public function __construct(
public int $currentPage,
public int $perPage,
public int $totalItemsCount,
public int $totalPagesCount,
public ?int $nextPage,
public ?int $previousPage
) {}
public static function fromArray(array $data): self
{
return new self(
$data['currentPage'],
$data['perPage'],
$data['totalItemsCount'],
$data['totalPagesCount'],
$data['nextPage'],
$data['previousPage']
);
}
}
class GroupsResponse
{
/**
* @param Group[] $data
*/
public function __construct(
private array $data,
private int $workspaceId,
private PaginationMeta $paginationMeta
) {}
/**
* @return Group[]
*/
public function getData(): array
{
return $this->data;
}
public function getWorkspaceId(): int
{
return $this->workspaceId;
}
public function getPaginationMeta(): PaginationMeta
{
return $this->paginationMeta;
}
}
class GroupsFetcher
{
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
public function fetchGroups(int $workspaceId): GroupsResponse
{
$url = sprintf(
'%s/v1/workspaces/%d/groups',
$this->baseUrl,
$workspaceId
);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
]
]);
$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new \RuntimeException('CURL Error: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode !== 200) {
throw new \RuntimeException("Failed to fetch groups: HTTP $httpCode");
}
$responseData = json_decode($responseBody, true);
return new GroupsResponse(
array_map(
fn($groupData) => Group::fromArray($groupData),
$responseData['data']
),
$responseData['meta']['workspaceId'],
PaginationMeta::fromArray($responseData['meta']['pagination'])
);
}
}
// Example usage
try {
$baseUrl = 'https://api.instapage.com';
$apiKey = 'your_api_key_here';
$workspaceId = 7068;
$fetcher = new GroupsFetcher($baseUrl, $apiKey);
$groupsResponse = $fetcher->fetchGroups($workspaceId);
// Process groups
foreach ($groupsResponse->getData() as $group) {
// Do something with each group
print_r($group->toArray());
}
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
The above request returns JSON structured like this:
{
"data": [
{
"id": 7356,
"name": "Medusa Dragon",
"pages": []
},
{
"id": 7355,
"name": "Speedball",
"pages": []
},
{
"id": 7340,
"name": "testg",
"pages": [
9399
]
}
],
"meta": {
"workspaceId": 7068,
"pagination": {
"currentPage": 1,
"perPage": 100,
"totalItemsCount": 16,
"totalPagesCount": 1,
"nextPage": null,
"previousPage": null
}
}
}
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
The ID of the workspace to retrieve groups for |
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Query Parameters
Parameter | Type | Default | Description |
---|---|---|---|
page |
number |
1 |
Page number for pagination |
Response JSON structure
JSON Path | Type | Description |
---|---|---|
data[].id |
number |
Unique identifier of the group |
data[].name |
string |
Name of the group |
data[].pages |
number[] |
Array of page IDs that belong to the group |
meta.workspaceId |
number |
ID of the workspace the groups belong to |
meta.pagination.currentPage |
number |
Current page number |
meta.pagination.perPage |
number |
Number of items per page |
meta.pagination.totalItemsCount |
number |
Total number of groups available |
meta.pagination.totalPagesCount |
number |
Total number of pages |
meta.pagination.nextPage |
number? |
Next page number (null if there is no next page) |
meta.pagination.previousPage |
number? |
Previous page number (null if on first page) |
Response status codes
Status | Description |
---|---|
200 |
The request was processed successfully |
400 |
Bad request. Validation error — review input parameters |
401 |
Unauthorized. Authentication failed or missing credentials |
403 |
Forbidden. The user does not have the necessary permissions |
404 |
Not Found. The requested workspace could not be located |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. Please slow down and retry after some time |
500 |
Internal Server Error. An unexpected condition prevented the request from being fulfilled |
Create Group
Create a new group (folder) in a workspace.
HTTP Request
https://api.instapage.com/v1/workspaces/{workspaceId}/groups
curl -X POST "https://api.instapage.com/v1/workspaces/{workspaceId}/groups" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "New Group"}'
async function createGroup(workspaceId, name, apiKey) {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/groups`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ name })
});
if (response.statusCode !== 201) {
throw new Error(`Failed to create group: ${response.statusText}`);
}
return await response.json();
}
type GroupResponse = {
data: {
id: number;
name: string;
pages: number[];
};
meta: {
workspaceId: number;
};
};
async function createGroup(
workspaceId: number,
name: string,
apiKey: string
): Promise<GroupResponse> {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/groups`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ name })
});
if (response.statusCode !== 201) {
throw new Error(`Failed to create group: ${response.statusText}`);
}
return await response.json();
}
<?php
declare(strict_types=1);
namespace InstapageGroupCreator;
readonly class Group
{
public function __construct(
public int $id,
public string $name,
public array $pages
) {}
public static function fromArray(array $data): self
{
return new self(
$data['id'],
$data['name'],
$data['pages'] ?? []
);
}
public function toArray(): array
{
return [
'id' => $this->id,
'name' => $this->name,
'pages' => $this->pages
];
}
}
class GroupCreator
{
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
public function createGroup(int $workspaceId, string $name): Group
{
$url = sprintf(
'%s/v1/workspaces/%d/groups',
$this->baseUrl,
$workspaceId
);
$payload = json_encode(['name' => $name]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
]
]);
$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new \RuntimeException('CURL Error: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode !== 201) {
throw new \RuntimeException("Failed to create group: HTTP $httpCode");
}
$responseData = json_decode($responseBody, true);
return Group::fromArray($responseData['data']);
}
}
// Example usage
try {
$baseUrl = 'https://api.instapage.com';
$apiKey = 'your_api_key_here';
$workspaceId = 7068;
$groupName = 'New Group';
$creator = new GroupCreator($baseUrl, $apiKey);
$newGroup = $creator->createGroup($workspaceId, $groupName);
echo "Group created with ID: " . $newGroup->id . " and name: " . $newGroup->name;
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
The above request returns JSON structured like this:
{
"data": {
"id": 7357,
"name": "New Group",
"pages": []
},
"meta": {
"workspaceId": 7068
}
}
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
The ID of the workspace to create the group in |
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Request Body
Parameter | Type | Required | Description |
---|---|---|---|
name |
string |
Yes | The name of the new group |
Response JSON structure
JSON Path | Type | Description |
---|---|---|
data.id |
number |
Unique identifier of the created group |
data.name |
string |
Name of the group |
data.pages |
number[] |
Array of page IDs (empty for new groups) |
meta.workspaceId |
number |
ID of the workspace the group belongs to |
Response status codes
Status | Description |
---|---|
201 |
The request was processed successfully |
400 |
Bad request. Invalid name or other parameter |
401 |
Unauthorized. Authentication failed or missing credentials |
403 |
Forbidden. The user does not have the necessary permissions |
404 |
Not Found. The requested workspace could not be located |
409 |
Conflict. A group with the same name already exists |
413 |
Payload Too Large. Group name exceeds character limit |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. Please slow down and retry after some time |
500 |
Internal Server Error. An unexpected condition prevented the request from being fulfilled |
Update Group
Update an existing group (folder) in a workspace.
HTTP Request
https://api.instapage.com/v1/workspaces/{workspaceId}/groups/{groupId}
curl -X PUT "https://api.instapage.com/v1/workspaces/{workspaceId}/groups/{groupId}" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Updated Group Name"}'
async function updateGroup(workspaceId, groupId, name, apiKey) {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/groups/${groupId}`;
const response = await fetch(url, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ name })
});
if (!response.ok) {
throw new Error(`Failed to update group: ${response.statusText}`);
}
return response.status === 200;
}
async function updateGroup(
workspaceId: number,
groupId: number,
name: string,
apiKey: string
): Promise<boolean> {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/groups/${groupId}`;
const response = await fetch(url, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ name })
});
if (!response.ok) {
throw new Error(`Failed to update group: ${response.statusText}`);
}
return response.status === 200;
}
<?php
declare(strict_types=1);
namespace InstapageGroupUpdater;
class GroupUpdater
{
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
public function updateGroup(int $workspaceId, int $groupId, string $name): bool
{
$url = sprintf(
'%s/v1/workspaces/%d/groups/%d',
$this->baseUrl,
$workspaceId,
$groupId
);
$payload = json_encode(['name' => $name]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
]
]);
$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new \RuntimeException('CURL Error: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode !== 200) {
throw new \RuntimeException("Failed to update group: HTTP $httpCode");
}
return true;
}
}
// Example usage
try {
$baseUrl = 'https://api.instapage.com';
$apiKey = 'your_api_key_here';
$workspaceId = 7068;
$groupId = 7357;
$newName = 'Updated Group Name';
$updater = new GroupUpdater($baseUrl, $apiKey);
$result = $updater->updateGroup($workspaceId, $groupId, $newName);
if ($result) {
echo "Group updated successfully.";
}
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
The ID of the workspace containing the group |
groupId |
number |
The ID of the group to update |
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Request Body
Parameter | Type | Required | Description |
---|---|---|---|
name |
string |
Yes | New name for the group |
Response status codes
Status | Description |
---|---|
200 |
The request was processed successfully |
400 |
Bad request. Invalid name or other parameter |
401 |
Unauthorized. Authentication failed or missing credentials |
403 |
Forbidden. The user does not have the necessary permissions |
404 |
Not Found. The group or workspace could not be located |
409 |
Conflict. A group with the same name already exists |
413 |
Payload Too Large. Group name exceeds character limit |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. Please slow down and retry after some time |
500 |
Internal Server Error. An unexpected condition prevented the request from being fulfilled |
Delete Group
Delete a group (folder) from a workspace.
HTTP Request
https://api.instapage.com/v1/workspaces/{workspaceId}/groups/{groupId}
curl -X DELETE "https://api.instapage.com/v1/workspaces/{workspaceId}/groups/{groupId}" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json"
async function deleteGroup(workspaceId, groupId, apiKey) {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/groups/${groupId}`;
const response = await fetch(url, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`Failed to delete group: ${response.statusText}`);
}
return response.status === 200;
}
async function deleteGroup(
workspaceId: number,
groupId: number,
apiKey: string
): Promise<boolean> {
const url = `https://api.instapage.com/v1/workspaces/${workspaceId}/groups/${groupId}`;
const response = await fetch(url, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`Failed to delete group: ${response.statusText}`);
}
return response.status === 200;
}
<?php
declare(strict_types=1);
namespace InstapageGroupDeleter;
class GroupDeleter
{
public function __construct(
private string $baseUrl,
private string $apiKey
) {}
public function deleteGroup(int $workspaceId, int $groupId): bool
{
$url = sprintf(
'%s/v1/workspaces/%d/groups/%d',
$this->baseUrl,
$workspaceId,
$groupId
);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: 'application/json'
]
]);
$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new \RuntimeException('CURL Error: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode !== 200) {
throw new \RuntimeException("Failed to delete group: HTTP $httpCode");
}
return true;
}
}
// Example usage
try {
$baseUrl = 'https://api.instapage.com';
$apiKey = 'your_api_key_here';
$workspaceId = 7068;
$groupId = 7357;
$deleter = new GroupDeleter($baseUrl, $apiKey);
$result = $deleter->deleteGroup($workspaceId, $groupId);
if ($result) {
echo "Group deleted successfully.";
}
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage();
}
Path Parameters
Parameter | Type | Description |
---|---|---|
workspaceId |
number |
The ID of the workspace containing the group |
groupId |
number |
The ID of the group to delete |
Headers
Content-Type: application/json
Authorization: Bearer TOKEN
Response status codes
Status | Description |
---|---|
200 |
The request was processed successfully and the group was deleted |
400 |
Bad request. Group contains pages and cannot be deleted |
401 |
Unauthorized. Authentication failed or missing credentials |
403 |
Forbidden. The user does not have the necessary permissions |
404 |
Not Found. The group or workspace could not be located |
429 |
Too Many Requests. The server is rejecting requests due to excessive rate of requests. Please slow down and retry after some time |
500 |
Internal Server Error. An unexpected condition prevented the request from being fulfilled |
Related articles
Instapage API Examples
This document provides examples of how to interact with the Instapage API using different programming languages. Each example demonstrates how to fetch workspaces, pages, form submissions, and analytics data from Instapage.
Table of Contents
- Example of collecting data from submissions and analysis and saving it as CSV
- Examples with submission delete from range
- Specialized Submission Deletion Examples
Example of collecting data from submissions and analysis and saving it as CSV
Node.js TypeScript Example
node -v # Should be v22.9.0
npm -v # Should be 10.8.3
node --experimental-strip-types example.ts # Replace example.ts with your file name
import fs from 'fs';
import { finished } from 'stream/promises';
import https from 'https';
class InstapageAPI {
constructor(token) {
this.token = token;
this.baseURL = 'https://api.instapage.com/v1';
}
getAllWorkspaces() {
return this.request('/workspaces');
}
request(endpoint, method, data) {
method = method || 'GET';
const url = this.baseURL + endpoint;
const options = {
method: method,
headers: {
'Authorization': 'Bearer ' + this.token,
'Content-Type': 'application/json'
}
};
return new Promise((resolve, reject) => {
const req = https.request(url, options, (res) => {
let responseBody = '';
res.on('data', (chunk) => {
responseBody += chunk;
});
res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(JSON.parse(responseBody));
} else {
reject(new Error('HTTP Error: ' + res.statusCode + ', URL: ' + url + ', RESPONSE: ' + responseBody));
}
});
});
req.on('error', (error) => {
reject(new Error('Request error: ' + error.message));
});
if (method === 'POST' && data) {
req.write(JSON.stringify(data));
}
req.end();
});
}
getAllPages(workspaceId) {
return this.request('/workspaces/' + workspaceId + '/pages');
}
getFormSubmissions(workspaceId, pageIds) {
const self = this;
return new Promise((resolve, reject) => {
let results = [];
const maxOfRounds = Math.ceil(pageIds.length / 100);
let currentRound = 0;
function processRound() {
if (currentRound >= maxOfRounds) {
resolve(results);
return;
}
const selectedPageIds = pageIds.slice(currentRound * 100, (currentRound + 1) * 100);
const data = {
pages: selectedPageIds,
timeframe: {
start: Math.floor(Date.now() / 1000) - 30 * 24 * 60 * 60,
end: Math.floor(Date.now() / 1000)
}
};
self.request('/workspaces/' + workspaceId + '/submissions', 'POST', data)
.then(function(response) {
results = results.concat(response.data.submissions);
currentRound++;
processRound();
})
.catch(reject);
}
processRound();
});
}
getAnalytics(workspaceId, pageIds) {
const self = this;
return new Promise((resolve, reject) => {
let results = [];
const maxOfRounds = Math.ceil(pageIds.length / 100);
let currentRound = 0;
function processRound() {
if (currentRound >= maxOfRounds) {
resolve(results);
return;
}
const selectedPageIds = pageIds.slice(currentRound * 100, (currentRound + 1) * 100);
const data = {
pages: selectedPageIds,
interval: 'daily',
timeframe: {
start: Math.floor(Date.now() / 1000) - 30 * 24 * 60 * 60,
end: Math.floor(Date.now() / 1000)
},
grouping: ['pageId']
};
self.request('/workspaces/' + workspaceId + '/analytics', 'POST', data)
.then(function(response) {
results = results.concat(response.data);
currentRound++;
processRound();
})
.catch(reject);
}
processRound();
});
}
}
function createCsv(filename, data, headers) {
return new Promise((resolve, reject) => {
const csvContent = [
headers.join(','),
...data.map(row => row.map(String).join(','))
].join('\n');
fs.writeFile(filename, csvContent, function(err) {
if (err) {
reject(err);
} else {
console.log('CSV file "' + filename + '" created successfully.');
resolve();
}
});
});
}
function main() {
const token = ''; // Replace with your actual API token
const api = new InstapageAPI(token);
api.getAllWorkspaces()
.then(function(response) {
const workspaces = response.data;
console.log('Found ' + workspaces.length + ' workspaces');
const allLeads = [];
const allAnalytics = [];
function processWorkspace(index) {
if (index >= workspaces.length) {
return Promise.all([
createCsv('leads.csv', allLeads, ['Workspace ID', 'Workspace Name', 'Page ID', 'Submission ID', 'Created At', 'Fields']),
createCsv('analytics.csv', allAnalytics, ['Workspace ID', 'Workspace Name', 'Page ID', 'Date', 'Visits', 'Conversions', 'Leads'])
]);
}
const workspace = workspaces[index];
console.log('Processing workspace: ' + workspace.workspaceName + ' (ID: ' + workspace.workspaceId + ')');
return api.getAllPages(workspace.workspaceId)
.then(function(response) {
const pages = response.data;
console.log('Found ' + pages.length + ' pages in workspace ' + workspace.workspaceName);
const pageIds = pages.map(function(page) { return page.id; });
return Promise.all([
api.getFormSubmissions(workspace.workspaceId, pageIds),
api.getAnalytics(workspace.workspaceId, pageIds)
]);
})
.then(function([submissions, analytics]) {
submissions.forEach(function(submission) {
allLeads.push([
workspace.workspaceId,
workspace.workspaceName,
submission.pageId,
submission.id,
submission.createdAt,
JSON.stringify(submission.fields)
]);
});
analytics.forEach(function(analytic) {
allAnalytics.push([
workspace.workspaceId,
workspace.workspaceName,
analytic.key.pageId,
analytic.key.date,
analytic.visit,
analytic.conversion,
analytic.leads
]);
});
return processWorkspace(index + 1);
});
}
return processWorkspace(0);
})
.then(function() {
console.log('All data processed successfully.');
})
.catch(function(error) {
console.error('An error occurred:', error.message);
});
}
main();
Code Overview
This example uses TypeScript with Node.js version 22.9.0. It demonstrates how to:
- Authenticate with the Instapage API
- Fetch all workspaces
- For each workspace, fetch all pages
- Get form submissions for each page
- Get analytics data for each page
- Export the collected data to CSV files
Key Components
InstapageAPI
class: Handles all API interactionscreateCsv
function: Exports data to CSV formatmain
function: Orchestrates the entire data collection process
Endpoints Used
/workspaces
: GET request to fetch all workspaces/workspaces/{workspaceId}/pages
: GET request to fetch all pages in a workspace/workspaces/{workspaceId}/submissions
: POST request to fetch form submissions/workspaces/{workspaceId}/analytics
: POST request to fetch analytics data
Results
The script generates two CSV files:
1. leads.csv
: Contains form submission data
2. analytics.csv
: Contains analytics data for each page
Node.js JavaScript Example
node -v # Should be v22.9.0
npm -v # Should be 10.8.3
node example.js # Replace example.js with your file name
const fs = require('fs');
const https = require('https');
class InstapageAPI {
constructor(token) {
this.token = token;
this.baseURL = 'https://api.instapage.com/v1';
}
getAllWorkspaces() {
return this.request('/workspaces');
}
request(endpoint, method, data) {
method = method || 'GET';
const url = this.baseURL + endpoint;
const options = {
method: method,
headers: {
'Authorization': 'Bearer ' + this.token,
'Content-Type': 'application/json'
}
};
return new Promise((resolve, reject) => {
const req = https.request(url, options, (res) => {
let responseBody = '';
res.on('data', (chunk) => {
responseBody += chunk;
});
res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(JSON.parse(responseBody));
} else {
reject(new Error('HTTP Error: ' + res.statusCode + ', URL: ' + url + ', RESPONSE: ' + responseBody));
}
});
});
req.on('error', (error) => {
reject(new Error('Request error: ' + error.message));
});
if (method === 'POST' && data) {
req.write(JSON.stringify(data));
}
req.end();
});
}
getAllPages(workspaceId) {
return this.request('/workspaces/' + workspaceId + '/pages');
}
getFormSubmissions(workspaceId, pageIds) {
const self = this;
return new Promise((resolve, reject) => {
let results = [];
const maxOfRounds = Math.ceil(pageIds.length / 100);
let currentRound = 0;
function processRound() {
if (currentRound >= maxOfRounds) {
resolve(results);
return;
}
const selectedPageIds = pageIds.slice(currentRound * 100, (currentRound + 1) * 100);
const data = {
pages: selectedPageIds,
timeframe: {
start: Math.floor(Date.now() / 1000) - 30 * 24 * 60 * 60,
end: Math.floor(Date.now() / 1000)
}
};
self.request('/workspaces/' + workspaceId + '/submissions', 'POST', data)
.then(function(response) {
results = results.concat(response.data.submissions);
currentRound++;
processRound();
})
.catch(reject);
}
processRound();
});
}
getAnalytics(workspaceId, pageIds) {
const self = this;
return new Promise((resolve, reject) => {
let results = [];
const maxOfRounds = Math.ceil(pageIds.length / 100);
let currentRound = 0;
function processRound() {
if (currentRound >= maxOfRounds) {
resolve(results);
return;
}
const selectedPageIds = pageIds.slice(currentRound * 100, (currentRound + 1) * 100);
const data = {
pages: selectedPageIds,
interval: 'daily',
timeframe: {
start: Math.floor(Date.now() / 1000) - 30 * 24 * 60 * 60,
end: Math.floor(Date.now() / 1000)
},
grouping: ['pageId']
};
self.request('/workspaces/' + workspaceId + '/analytics', 'POST', data)
.then(function(response) {
results = results.concat(response.data);
currentRound++;
processRound();
})
.catch(reject);
}
processRound();
});
}
}
function createCsv(filename, data, headers) {
return new Promise((resolve, reject) => {
const csvContent = [
headers.join(','),
...data.map(row => row.map(String).join(','))
].join('\n');
fs.writeFile(filename, csvContent, function(err) {
if (err) {
reject(err);
} else {
console.log('CSV file "' + filename + '" created successfully.');
resolve();
}
});
});
}
function main() {
const token = ''; // Replace with your actual API token
const api = new InstapageAPI(token);
api.getAllWorkspaces()
.then(function(response) {
const workspaces = response.data;
console.log('Found ' + workspaces.length + ' workspaces');
const allLeads = [];
const allAnalytics = [];
function processWorkspace(index) {
if (index >= workspaces.length) {
return Promise.all([
createCsv('leads.csv', allLeads, ['Workspace ID', 'Workspace Name', 'Page ID', 'Submission ID', 'Created At', 'Fields']),
createCsv('analytics.csv', allAnalytics, ['Workspace ID', 'Workspace Name', 'Page ID', 'Date', 'Visits', 'Conversions', 'Leads'])
]);
}
const workspace = workspaces[index];
console.log('Processing workspace: ' + workspace.workspaceName + ' (ID: ' + workspace.workspaceId + ')');
return api.getAllPages(workspace.workspaceId)
.then(function(response) {
const pages = response.data;
console.log('Found ' + pages.length + ' pages in workspace ' + workspace.workspaceName);
const pageIds = pages.map(function(page) { return page.id; });
return Promise.all([
api.getFormSubmissions(workspace.workspaceId, pageIds),
api.getAnalytics(workspace.workspaceId, pageIds)
]);
})
.then(function(results) {
const submissions = results[0];
const analytics = results[1];
submissions.forEach(function(submission) {
allLeads.push([
workspace.workspaceId,
workspace.workspaceName,
submission.pageId,
submission.id,
submission.createdAt,
JSON.stringify(submission.fields)
]);
});
analytics.forEach(function(analytic) {
allAnalytics.push([
workspace.workspaceId,
workspace.workspaceName,
analytic.key.pageId,
analytic.key.date,
analytic.visit,
analytic.conversion,
analytic.leads
]);
});
return processWorkspace(index + 1);
});
}
return processWorkspace(0);
})
.then(function() {
console.log('All data processed successfully.');
})
.catch(function(error) {
console.error('An error occurred:', error.message);
});
}
main();
This example is similar to the TypeScript version but uses plain JavaScript. The functionality and endpoints used are the same.
PHP Example
php -v # Should be PHP 7.4.33 (cli)
php example.php # Replace example.php with your file name
<?php
class InstapageAPI
{
private $token;
private $baseURL = 'https://api.instapage.com/v1';
public function __construct($token)
{
$this->token = $token;
}
public function getAllWorkspaces()
{
$response = $this->request('/workspaces');
return $response['data'];
}
private function request($endpoint, $method = 'GET', $data = null)
{
$url = $this->baseURL . $endpoint;
$headers = [
'Authorization: Bearer ' . $this->token,
'Content-Type: application/json'
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
if ($data) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
}
$response = curl_exec($ch);
if (curl_errno($ch)) {
throw new Exception('Curl error: ' . curl_error($ch));
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode >= 200 && $httpCode < 300) {
return json_decode($response, true);
} else {
throw new Exception("HTTP Error: $httpCode, URL: $url, RESPONSE: $response");
}
}
public function getAllPages($workspaceId)
{
$response = $this->request("/workspaces/$workspaceId/pages");
return $response['data'];
}
public function getFormSubmissions($workspaceId, $pageIds)
{
$results = [];
$currentRound = 0;
$maxOfRounds = ceil(count($pageIds) / 100);
while ($currentRound < $maxOfRounds) {
$selectedPageIds = array_slice($pageIds, $currentRound * 100, 100);
$data = [
'pages' => $selectedPageIds,
'timeframe' => [
'start' => strtotime('-30 days'),
'end' => time()
]
];
$response = $this->request("/workspaces/$workspaceId/submissions", 'POST', $data);
$results = [...$results, ...$response['data']['submissions']];
$currentRound += 1;
}
return $results;
}
public function getAnalytics($workspaceId, $pageIds)
{
$results = [];
$currentRound = 0;
$maxOfRounds = ceil(count($pageIds) / 100);
while ($currentRound < $maxOfRounds) {
$selectedPageIds = array_slice($pageIds, $currentRound * 100, 100);
$data = [
'pages' => $selectedPageIds,
'interval' => 'daily',
'timeframe' => [
'start' => strtotime('-30 days'),
'end' => time()
],
'grouping' => ['pageId']
];
$response = $this->request("/workspaces/$workspaceId/analytics", 'POST', $data);
$results = [...$results, ...$response['data']];
$currentRound += 1;
}
return $results;
}
}
function createCsv($filename, $data, $headers)
{
$fp = fopen($filename, 'w');
fputcsv($fp, $headers);
foreach ($data as $row) {
fputcsv($fp, $row);
}
fclose($fp);
echo "CSV file '$filename' created successfully.\n";
}
function main()
{
$token = ''; // Replace with your actual API token
$api = new InstapageAPI($token);
try {
$workspaces = $api->getAllWorkspaces();
echo "Found " . count($workspaces) . " workspaces\n";
$allLeads = [];
$allAnalytics = [];
foreach ($workspaces as $workspace) {
echo "Processing workspace: {$workspace['workspaceName']} (ID: {$workspace['workspaceId']})\n";
$pages = $api->getAllPages($workspace['workspaceId']);
echo "Found " . count($pages) . " pages in workspace {$workspace['workspaceName']}\n";
$pageIds = array_column($pages, 'id');
// Fetch and process leads
$submissions = $api->getFormSubmissions($workspace['workspaceId'], $pageIds);
foreach ($submissions as $submission) {
$allLeads[] = [
$workspace['workspaceId'],
$workspace['workspaceName'],
$submission['pageId'],
$submission['id'],
$submission['createdAt'],
json_encode($submission['fields'])
];
}
// Fetch and process analytics
$analytics = $api->getAnalytics($workspace['workspaceId'], $pageIds);
foreach ($analytics as $analytic) {
$allAnalytics[] = [
$workspace['workspaceId'],
$workspace['workspaceName'],
$analytic['key']['pageId'],
$analytic['key']['date'],
$analytic['visit'],
$analytic['conversion'],
$analytic['leads']
];
}
}
// Create CSV for leads
createCsv('leads.csv', $allLeads, ['Workspace ID', 'Workspace Name', 'Page ID', 'Submission ID', 'Created At', 'Fields']);
// Create CSV for analytics
createCsv('analytics.csv', $allAnalytics, ['Workspace ID', 'Workspace Name', 'Page ID', 'Date', 'Visits', 'Conversions', 'Leads']);
} catch (Exception $e) {
echo "An error occurred: " . $e->getMessage() . "\n";
}
}
main();
Code Overview
This example uses PHP 7.4.33 and demonstrates the same functionality as the Node.js examples.
Key Differences
- Uses cURL for HTTP requests instead of Node's built-in https module
- PHP-specific syntax and conventions
Conclusion
These examples demonstrate how to interact with the Instapage API to collect workspace, page, form submission, and analytics data. The process is similar across all three implementations (TypeScript, JavaScript, and PHP), with the main differences being in language-specific syntax and HTTP request handling.
Remember to replace the empty token string with your actual Instapage API token before running any of these scripts. Also, ensure you have the necessary permissions to access the data you're requesting through the API.
Examples with submission delete from range
These examples demonstrate how to fetch submissions from a specific date range and then delete them. This can be useful for data cleanup or compliance purposes.
Node.js TypeScript Example
node -v # Should be v22.9.0
npm -v # Should be 10.8.3
node --experimental-strip-types delete_submissions.ts
import https from 'https';
class InstapageAPI {
private token: string;
private baseURL: string;
constructor(token: string) {
this.token = token;
this.baseURL = 'https://api.instapage.com/v1';
}
request(endpoint: string, method: string = 'GET', data?: any): Promise<any> {
const url = this.baseURL + endpoint;
const options = {
method: method,
headers: {
'Authorization': 'Bearer ' + this.token,
'Content-Type': 'application/json'
}
};
return new Promise((resolve, reject) => {
const req = https.request(url, options, (res) => {
let responseBody = '';
res.on('data', (chunk) => {
responseBody += chunk;
});
res.on('end', () => {
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
try {
const parsedResponse = responseBody ? JSON.parse(responseBody) : {};
resolve(parsedResponse);
} catch (e) {
reject(new Error(`Failed to parse response: ${e.message}`));
}
} else {
reject(new Error(`HTTP Error: ${res.statusCode}, URL: ${url}, RESPONSE: ${responseBody}`));
}
});
});
req.on('error', (error) => {
reject(new Error(`Request error: ${error.message}`));
});
if ((method === 'POST' || method === 'DELETE') && data) {
req.write(JSON.stringify(data));
}
req.end();
});
}
getAllWorkspaces(): Promise<any> {
return this.request('/workspaces');
}
getAllPages(workspaceId: string): Promise<any> {
return this.request(`/workspaces/${workspaceId}/pages`);
}
async getAllSubmissionsFromRange(
workspaceId: string,
pageIds: string[],
startDate: string,
endDate: string
): Promise<Submission[]> {
let allSubmissions: Submission[] = [];
let nextPageToken: string | null = null;
do {
const data = {
pages: pageIds,
timeframe: {
start: Math.floor(new Date(startDate).getTime() / 1000),
end: Math.floor(new Date(endDate).getTime() / 1000)
},
nextPageToken: nextPageToken
};
const response = await this.request(`/workspaces/${workspaceId}/submissions`, 'POST', data);
// Add submissions from current page to our collection
if (response.data.submissions && response.data.submissions.length > 0) {
allSubmissions = [...allSubmissions, ...response.data.submissions];
}
// Get the next page token for pagination
nextPageToken = response.meta?.nextPageToken || null;
if (nextPageToken) {
console.log(`Fetching next page of submissions with token: ${nextPageToken}`);
}
} while (nextPageToken);
return allSubmissions;
}
deleteSubmissions(workspaceId: string, submissionIds: string[]): Promise<any> {
const data = {
submissions: submissionIds
};
return this.request(`/workspaces/${workspaceId}/submissions`, 'DELETE', data);
}
}
interface Page {
id: string;
name: string;
}
interface Submission {
id: string;
pageId: string;
createdAt: string;
fields: Record<string, any>;
}
async function main() {
const token = ''; // Replace with your actual API token
const api = new InstapageAPI(token);
// Configure the workspace ID and date range
const workspaceId = ''; // Replace with your workspace ID
const startDate = '2025-03-01'; // Format: YYYY-MM-DD
const endDate = '2025-03-31'; // Format: YYYY-MM-DD
try {
// Step 1: Get all pages for the workspace
console.log('Fetching pages from workspace...');
const pagesResponse = await api.getAllPages(workspaceId);
const pages: Page[] = pagesResponse.data;
const pageIds = pages.map(page => page.id);
if (pageIds.length === 0) {
console.log('No pages found in the workspace.');
return;
}
console.log(`Found ${pageIds.length} pages.`);
// Step 2: Get all submissions within the date range with pagination
console.log(`Fetching submissions from ${startDate} to ${endDate}...`);
const submissions = await api.getAllSubmissionsFromRange(workspaceId, pageIds, startDate, endDate);
if (submissions.length === 0) {
console.log('No submissions found in the specified date range.');
return;
}
console.log(`Found ${submissions.length} submissions across all pages.`);
// Creating a summary of submissions by page
const submissionsByPage: Record<string, number> = {};
submissions.forEach(submission => {
if (!submissionsByPage[submission.pageId]) {
submissionsByPage[submission.pageId] = 0;
}
submissionsByPage[submission.pageId]++;
});
console.log('Submissions by page:');
Object.entries(submissionsByPage).forEach(([pageId, count]) => {
const page = pages.find(p => p.id === pageId);
console.log(`- ${page ? page.name : 'Unknown page'} (${pageId}): ${count} submissions`);
});
// Step 3: Prepare for deletion
// Note: The API has a limit of 100 submission IDs per delete request
const submissionIds = submissions.map(submission => submission.id);
const batchSize = 100;
const batches = [];
// Split submission IDs into batches of 100
for (let i = 0; i < submissionIds.length; i += batchSize) {
batches.push(submissionIds.slice(i, i + batchSize));
}
console.log(`Prepared ${batches.length} batches for deletion (max 100 submissions per batch).`);
// DANGER ZONE: Uncomment the lines below to actually delete the submissions
// for (let i = 0; i < batches.length; i++) {
// const batch = batches[i];
// console.log(`Deleting batch ${i + 1}/${batches.length} (${batch.length} submissions)...`);
// await api.deleteSubmissions(workspaceId, batch);
// console.log(`Successfully deleted batch ${i + 1}/${batches.length}.`);
// }
// console.log(`Successfully deleted all ${submissionIds.length} submissions.`);
// Safe output - just showing what would be deleted
console.log(`Would delete ${submissionIds.length} submissions in ${batches.length} batches.`);
console.log('To actually delete the submissions, uncomment the "DANGER ZONE" section in the code.');
} catch (error) {
console.error('An error occurred:', error instanceof Error ? error.message : String(error));
}
}
main();
Code Overview
This TypeScript example demonstrates how to:
- Authenticate with the Instapage API
- Fetch all pages within a specific workspace
- Get submissions from a specific date range
- Show a summary of submissions by page
- Delete the submissions (with safety measures to prevent accidental deletion)
Key Features
- Strong typing with TypeScript interfaces
- Type-safe API client implementation
- Modern async/await pattern for async operations
- Error handling with typed exceptions
Endpoints Used
/workspaces/{workspaceId}/pages
: GET request to fetch all pages in a workspace/workspaces/{workspaceId}/submissions
: POST request to fetch form submissions within a date range/workspaces/{workspaceId}/submissions
: DELETE request to delete specific submissions
Node.js JavaScript Example
node -v # Should be v22.9.0
npm -v # Should be 10.8.3
node delete_submissions.js
const https = require('https');
class InstapageAPI {
constructor(token) {
this.token = token;
this.baseURL = 'https://api.instapage.com/v1';
}
request(endpoint, method, data) {
method = method || 'GET';
const url = this.baseURL + endpoint;
const options = {
method: method,
headers: {
'Authorization': 'Bearer ' + this.token,
'Content-Type': 'application/json'
}
};
return new Promise((resolve, reject) => {
const req = https.request(url, options, (res) => {
let responseBody = '';
res.on('data', (chunk) => {
responseBody += chunk;
});
res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
try {
const parsedResponse = responseBody ? JSON.parse(responseBody) : {};
resolve(parsedResponse);
} catch (e) {
reject(new Error('Failed to parse response: ' + e.message));
}
} else {
reject(new Error('HTTP Error: ' + res.statusCode + ', URL: ' + url + ', RESPONSE: ' + responseBody));
}
});
});
req.on('error', (error) => {
reject(new Error('Request error: ' + error.message));
});
if ((method === 'POST' || method === 'DELETE') && data) {
req.write(JSON.stringify(data));
}
req.end();
});
}
getAllWorkspaces() {
return this.request('/workspaces');
}
getAllPages(workspaceId) {
return this.request('/workspaces/' + workspaceId + '/pages');
}
async getAllSubmissionsFromRange(workspaceId, pageIds, startDate, endDate) {
let allSubmissions = [];
let nextPageToken = null;
do {
const data = {
pages: pageIds,
timeframe: {
start: Math.floor(new Date(startDate).getTime() / 1000),
end: Math.floor(new Date(endDate).getTime() / 1000)
},
nextPageToken: nextPageToken
};
const response = await this.request('/workspaces/' + workspaceId + '/submissions', 'POST', data);
// Add submissions from current page to our collection
if (response.data.submissions && response.data.submissions.length > 0) {
allSubmissions = [...allSubmissions, ...response.data.submissions];
}
// Get the next page token for pagination
nextPageToken = response.meta?.nextPageToken || null;
if (nextPageToken) {
console.log(`Fetching next page of submissions with token: ${nextPageToken}`);
}
} while (nextPageToken);
return allSubmissions;
}
deleteSubmissions(workspaceId, submissionIds) {
const data = {
submissions: submissionIds
};
return this.request('/workspaces/' + workspaceId + '/submissions', 'DELETE', data);
}
}
async function main() {
const token = ''; // Replace with your actual API token
const api = new InstapageAPI(token);
// Configure the workspace ID and date range
const workspaceId = ''; // Replace with your workspace ID
const startDate = '2025-03-01'; // Format: YYYY-MM-DD
const endDate = '2025-03-31'; // Format: YYYY-MM-DD
try {
// Step 1: Get all pages for the workspace
console.log('Fetching pages from workspace...');
const pagesResponse = await api.getAllPages(workspaceId);
const pages = pagesResponse.data;
const pageIds = pages.map(page => page.id);
if (pageIds.length === 0) {
console.log('No pages found in the workspace.');
return;
}
console.log(`Found ${pageIds.length} pages.`);
// Step 2: Get all submissions within the date range with pagination
console.log(`Fetching submissions from ${startDate} to ${endDate}...`);
const submissions = await api.getAllSubmissionsFromRange(workspaceId, pageIds, startDate, endDate);
if (submissions.length === 0) {
console.log('No submissions found in the specified date range.');
return;
}
console.log(`Found ${submissions.length} submissions across all pages.`);
// Creating a summary of submissions by page
const submissionsByPage = {};
submissions.forEach(submission => {
if (!submissionsByPage[submission.pageId]) {
submissionsByPage[submission.pageId] = 0;
}
submissionsByPage[submission.pageId]++;
});
console.log('Submissions by page:');
Object.entries(submissionsByPage).forEach(([pageId, count]) => {
const page = pages.find(p => p.id === pageId);
console.log(`- ${page ? page.name : 'Unknown page'} (${pageId}): ${count} submissions`);
});
// Step 3: Prepare for deletion
// Note: The API has a limit of 100 submission IDs per delete request
const submissionIds = submissions.map(submission => submission.id);
const batchSize = 100;
const batches = [];
// Split submission IDs into batches of 100
for (let i = 0; i < submissionIds.length; i += batchSize) {
batches.push(submissionIds.slice(i, i + batchSize));
}
console.log(`Prepared ${batches.length} batches for deletion (max 100 submissions per batch).`);
// DANGER ZONE: Uncomment the lines below to actually delete the submissions
// for (let i = 0; i < batches.length; i++) {
// const batch = batches[i];
// console.log(`Deleting batch ${i + 1}/${batches.length} (${batch.length} submissions)...`);
// await api.deleteSubmissions(workspaceId, batch);
// console.log(`Successfully deleted batch ${i + 1}/${batches.length}.`);
// }
// console.log(`Successfully deleted all ${submissionIds.length} submissions.`);
// Safe output - just showing what would be deleted
console.log(`Would delete ${submissionIds.length} submissions in ${batches.length} batches.`);
console.log('To actually delete the submissions, uncomment the "DANGER ZONE" section in the code.');
} catch (error) {
console.error('An error occurred:', error.message);
}
}
main();
Code Overview
This Node.js example demonstrates how to:
- Authenticate with the Instapage API
- Fetch all pages within a specific workspace
- Get submissions from a specific date range
- Show a summary of submissions by page
- Delete the submissions (with safety measures to prevent accidental deletion)
Key Components
InstapageAPI
class: Handles all API interactionsgetSubmissionsFromRange
: Fetches submissions within a specified date rangedeleteSubmissions
: Deletes multiple submissions by ID
Endpoints Used
/workspaces/{workspaceId}/pages
: GET request to fetch all pages in a workspace/workspaces/{workspaceId}/submissions
: POST request to fetch form submissions within a date range/workspaces/{workspaceId}/submissions
: DELETE request to delete specific submissions
PHP Example
php -v # Should be PHP 7.4.33 (cli)
php delete_submissions.php
<?php
class InstapageAPI
{
private $token;
private $baseURL = 'https://api.instapage.com/v1';
public function __construct($token)
{
$this->token = $token;
}
private function request($endpoint, $method = 'GET', $data = null)
{
$url = $this->baseURL . $endpoint;
$headers = [
'Authorization: Bearer ' . $this->token,
'Content-Type: application/json'
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
if ($data) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
} elseif ($method === 'DELETE') {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
if ($data) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
}
$response = curl_exec($ch);
if (curl_errno($ch)) {
throw new Exception('Curl error: ' . curl_error($ch));
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode >= 200 && $httpCode < 300) {
return $response ? json_decode($response, true) : [];
} else {
throw new Exception("HTTP Error: $httpCode, URL: $url, RESPONSE: $response");
}
}
public function getAllPages($workspaceId)
{
$response = $this->request("/workspaces/$workspaceId/pages");
return $response['data'];
}
public function getAllSubmissionsFromRange($workspaceId, $pageIds, $startDate, $endDate)
{
$allSubmissions = [];
$nextPageToken = null;
do {
$data = [
'pages' => $pageIds,
'timeframe' => [
'start' => strtotime($startDate),
'end' => strtotime($endDate)
]
];
// Add next page token if available
if ($nextPageToken) {
$data['nextPageToken'] = $nextPageToken;
}
$response = $this->request("/workspaces/$workspaceId/submissions", 'POST', $data);
// Add submissions from current page to our collection
if (!empty($response['data']['submissions'])) {
$allSubmissions = array_merge($allSubmissions, $response['data']['submissions']);
}
// Get the next page token for pagination
$nextPageToken = isset($response['meta']['nextPageToken']) ? $response['meta']['nextPageToken'] : null;
if ($nextPageToken) {
echo "Fetching next page of submissions with token: $nextPageToken\n";
}
} while ($nextPageToken);
return $allSubmissions;
}
public function deleteSubmissions($workspaceId, $submissionIds)
{
$data = [
'submissions' => $submissionIds
];
return $this->request("/workspaces/$workspaceId/submissions", 'DELETE', $data);
}
}
function main()
{
$token = ''; // Replace with your actual API token
$api = new InstapageAPI($token);
// Configure the workspace ID and date range
$workspaceId = ''; // Replace with your workspace ID
$startDate = '2025-03-01'; // Format: YYYY-MM-DD
$endDate = '2025-03-31'; // Format: YYYY-MM-DD
try {
// Step 1: Get all pages for the workspace
echo "Fetching pages from workspace...\n";
$pages = $api->getAllPages($workspaceId);
$pageIds = array_column($pages, 'id');
if (empty($pageIds)) {
echo "No pages found in the workspace.\n";
return;
}
echo "Found " . count($pageIds) . " pages.\n";
// Step 2: Get all submissions within the date range with pagination
echo "Fetching submissions from $startDate to $endDate...\n";
$submissions = $api->getAllSubmissionsFromRange($workspaceId, $pageIds, $startDate, $endDate);
if (empty($submissions)) {
echo "No submissions found in the specified date range.\n";
return;
}
echo "Found " . count($submissions) . " submissions across all pages.\n";
// Creating a summary of submissions by page
$submissionsByPage = [];
foreach ($submissions as $submission) {
if (!isset($submissionsByPage[$submission['pageId']])) {
$submissionsByPage[$submission['pageId']] = 0;
}
$submissionsByPage[$submission['pageId']]++;
}
echo "Submissions by page:\n";
foreach ($submissionsByPage as $pageId => $count) {
$pageName = "Unknown page";
foreach ($pages as $page) {
if ($page['id'] === $pageId) {
$pageName = $page['name'];
break;
}
}
echo "- $pageName ($pageId): $count submissions\n";
}
// Step 3: Prepare for deletion
// Note: The API has a limit of 100 submission IDs per delete request
$submissionIds = array_column($submissions, 'id');
$batchSize = 100;
$batches = [];
// Split submission IDs into batches of 100
for ($i = 0; $i < count($submissionIds); $i += $batchSize) {
$batches[] = array_slice($submissionIds, $i, $batchSize);
}
echo "Prepared " . count($batches) . " batches for deletion (max 100 submissions per batch).\n";
// DANGER ZONE: Uncomment the lines below to actually delete the submissions
// for ($i = 0; $i < count($batches); $i++) {
// $batch = $batches[$i];
// echo "Deleting batch " . ($i + 1) . "/" . count($batches) . " (" . count($batch) . " submissions)...\n";
// $api->deleteSubmissions($workspaceId, $batch);
// echo "Successfully deleted batch " . ($i + 1) . "/" . count($batches) . ".\n";
// }
// echo "Successfully deleted all " . count($submissionIds) . " submissions.\n";
// Safe output - just showing what would be deleted
echo "Would delete " . count($submissionIds) . " submissions in " . count($batches) . " batches.\n";
echo "To actually delete the submissions, uncomment the \"DANGER ZONE\" section in the code.\n";
} catch (Exception $e) {
echo "An error occurred: " . $e->getMessage() . "\n";
}
}
main();
Code Overview
This PHP example demonstrates the same functionality as the Node.js version:
- Authenticate with the Instapage API
- Fetch all pages within a specific workspace
- Get submissions from a specific date range
- Show a summary of submissions by page
- Delete the submissions (with safety measures to prevent accidental deletion)
Key Differences
- Uses cURL for HTTP requests instead of Node's built-in https module
- PHP-specific syntax and conventions
- Error handling is done with try/catch blocks
Both examples include safety measures to prevent accidental deletion of submissions. By default, they will only show what would be deleted without actually performing the deletion. To enable deletion, uncomment the marked "DANGER ZONE" section in the code.
Specialized Submission Deletion Examples
The following examples demonstrate specialized methods for submission deletion based on different time criteria. These methods can be integrated into any of the above examples to provide more targeted data cleanup.
Example 1: Remove Submissions from a Specific Year
Example only in typescript, change tab to typescript
//Example 1: Remove Submissions from a Specific Year
// Method to get and delete submissions from a specific year
async function removeSubmissionsFromYear(api: InstapageAPI, workspaceId: string, pageId: string, year: number): Promise<void> {
// Calculate start and end timestamps for the specified year
const startDate = `${year}-01-01`;
const endDate = `${year}-12-31`;
console.log(`Fetching submissions for page ${pageId} from year ${year}...`);
// Get submissions for the specified year with pagination
let allSubmissions: Submission[] = [];
let nextPageToken: string | null = null;
do {
const data = {
pages: [pageId],
timeframe: {
start: Math.floor(new Date(startDate).getTime() / 1000),
end: Math.floor(new Date(endDate).getTime() / 1000)
},
nextPageToken: nextPageToken
};
const response = await api.request(`/workspaces/${workspaceId}/submissions`, 'POST', data);
// Add submissions from current page to our collection
if (response.data.submissions && response.data.submissions.length > 0) {
allSubmissions = [...allSubmissions, ...response.data.submissions];
}
// Get the next page token for pagination
nextPageToken = response.meta?.nextPageToken || null;
if (nextPageToken) {
console.log(`Fetching next page of submissions with token: ${nextPageToken}`);
}
} while (nextPageToken);
if (allSubmissions.length === 0) {
console.log(`No submissions found for year ${year}.`);
return;
}
console.log(`Found ${allSubmissions.length} submissions from year ${year}.`);
// Extract submission IDs
const submissionIds = allSubmissions.map(submission => submission.id);
// Prepare for batch deletion (API has a limit of 100 IDs per request)
const batchSize = 100;
const batches = [];
// Split submission IDs into batches of 100
for (let i = 0; i < submissionIds.length; i += batchSize) {
batches.push(submissionIds.slice(i, i + batchSize));
}
console.log(`Prepared ${batches.length} batches for deletion (max 100 submissions per batch).`);
// DANGER ZONE: Uncomment to delete submissions
// for (let i = 0; i < batches.length; i++) {
// const batch = batches[i];
// console.log(`Deleting batch ${i + 1}/${batches.length} (${batch.length} submissions)...`);
// await api.deleteSubmissions(workspaceId, batch);
// console.log(`Successfully deleted batch ${i + 1}/${batches.length}.`);
// }
// console.log(`Successfully deleted all ${submissionIds.length} submissions from year ${year}.`);
// Safe output
console.log(`Would delete ${submissionIds.length} submissions from year ${year} in ${batches.length} batches.`);
}
// Usage example
// await removeSubmissionsFromYear(api, "workspace-id", "page-id", 2024);
Example 2: Remove Submissions from a Specific Period
Example only in typescript, change tab to typescript
// Example 2: Remove Submissions from a Specific Period
// Method to get and delete submissions from a specific time period
async function removeSubmissionsFromPeriod(
api: InstapageAPI,
workspaceId: string,
pageId: string,
startDate: string,
endDate: string
): Promise<void> {
console.log(`Fetching submissions for page ${pageId} from ${startDate} to ${endDate}...`);
// Get submissions for the specified period with pagination
let allSubmissions: Submission[] = [];
let nextPageToken: string | null = null;
do {
const data = {
pages: [pageId],
timeframe: {
start: Math.floor(new Date(startDate).getTime() / 1000),
end: Math.floor(new Date(endDate).getTime() / 1000)
},
nextPageToken: nextPageToken
};
const response = await api.request(`/workspaces/${workspaceId}/submissions`, 'POST', data);
// Add submissions from current page to our collection
if (response.data.submissions && response.data.submissions.length > 0) {
allSubmissions = [...allSubmissions, ...response.data.submissions];
}
// Get the next page token for pagination
nextPageToken = response.meta?.nextPageToken || null;
if (nextPageToken) {
console.log(`Fetching next page of submissions with token: ${nextPageToken}`);
}
} while (nextPageToken);
if (allSubmissions.length === 0) {
console.log(`No submissions found for the specified period.`);
return;
}
console.log(`Found ${allSubmissions.length} submissions in the specified period.`);
// Extract submission IDs
const submissionIds = allSubmissions.map(submission => submission.id);
// Prepare for batch deletion (API has a limit of 100 IDs per request)
const batchSize = 100;
const batches = [];
// Split submission IDs into batches of 100
for (let i = 0; i < submissionIds.length; i += batchSize) {
batches.push(submissionIds.slice(i, i + batchSize));
}
console.log(`Prepared ${batches.length} batches for deletion (max 100 submissions per batch).`);
// DANGER ZONE: Uncomment to delete submissions
// for (let i = 0; i < batches.length; i++) {
// const batch = batches[i];
// console.log(`Deleting batch ${i + 1}/${batches.length} (${batch.length} submissions)...`);
// await api.deleteSubmissions(workspaceId, batch);
// console.log(`Successfully deleted batch ${i + 1}/${batches.length}.`);
// }
// console.log(`Successfully deleted all ${submissionIds.length} submissions from the specified period.`);
// Safe output
console.log(`Would delete ${submissionIds.length} submissions from the specified period in ${batches.length} batches.`);
}
// Usage example
// await removeSubmissionsFromPeriod(api, "workspace-id", "page-id", "2025-01-01", "2025-01-31");
Example 3: Remove Submissions Older Than a Specific Date
Example only in typescript, change tab to typescript
// Example 3: Remove Submissions Older Than a Specific Date
// Method to get and delete submissions older than a specific date
async function removeSubmissionsOlderThan(
api: InstapageAPI,
workspaceId: string,
pageId: string,
dateFrom: string
): Promise<void> {
// For older than, we need to use an early start date and the specified date as the end
const startDate = "2000-01-01"; // Arbitrary early date to capture all older submissions
const endDate = dateFrom;
console.log(`Fetching submissions for page ${pageId} older than ${dateFrom}...`);
// Get submissions older than the specified date with pagination
let allSubmissions: Submission[] = [];
let nextPageToken: string | null = null;
do {
const data = {
pages: [pageId],
timeframe: {
start: Math.floor(new Date(startDate).getTime() / 1000),
end: Math.floor(new Date(endDate).getTime() / 1000)
},
nextPageToken: nextPageToken
};
const response = await api.request(`/workspaces/${workspaceId}/submissions`, 'POST', data);
// Add submissions from current page to our collection
if (response.data.submissions && response.data.submissions.length > 0) {
allSubmissions = [...allSubmissions, ...response.data.submissions];
}
// Get the next page token for pagination
nextPageToken = response.meta?.nextPageToken || null;
if (nextPageToken) {
console.log(`Fetching next page of submissions with token: ${nextPageToken}`);
}
} while (nextPageToken);
if (allSubmissions.length === 0) {
console.log(`No submissions found older than ${dateFrom}.`);
return;
}
console.log(`Found ${allSubmissions.length} submissions older than ${dateFrom}.`);
// Extract submission IDs
const submissionIds = allSubmissions.map(submission => submission.id);
// Prepare for batch deletion (API has a limit of 100 IDs per request)
const batchSize = 100;
const batches = [];
// Split submission IDs into batches of 100
for (let i = 0; i < submissionIds.length; i += batchSize) {
batches.push(submissionIds.slice(i, i + batchSize));
}
console.log(`Prepared ${batches.length} batches for deletion (max 100 submissions per batch).`);
// DANGER ZONE: Uncomment to delete submissions
// for (let i = 0; i < batches.length; i++) {
// const batch = batches[i];
// console.log(`Deleting batch ${i + 1}/${batches.length} (${batch.length} submissions)...`);
// await api.deleteSubmissions(workspaceId, batch);
// console.log(`Successfully deleted batch ${i + 1}/${batches.length}.`);
// }
// console.log(`Successfully deleted all ${submissionIds.length} older submissions.`);
// Safe output
console.log(`Would delete ${submissionIds.length} submissions older than ${dateFrom} in ${batches.length} batches.`);
}
// Usage example
// await removeSubmissionsOlderThan(api, "workspace-id", "page-id", "2024-01-01");
Example 4: Batch Processing with Rate Limit Handling
When dealing with a large number of submissions, it's important to handle API rate limits. This example demonstrates how to delete submissions in batches while respecting rate limits: Example only in typescript, change tab to typescript
// Example 4: Batch Processing with Rate Limit Handling
// Method to delete submissions in batches with rate limit handling
async function batchDeleteWithRateLimit(
api: InstapageAPI,
workspaceId: string,
submissionIds: string[],
batchSize: number = 100,
delayMs: number = 1000
): Promise<void> {
if (submissionIds.length === 0) {
console.log('No submissions to delete.');
return;
}
const totalBatches = Math.ceil(submissionIds.length / batchSize);
console.log(`Preparing to delete ${submissionIds.length} submissions in ${totalBatches} batches of ${batchSize}...`);
// Helper function to delay execution (for rate limiting)
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
// Process in batches
for (let i = 0; i < totalBatches; i++) {
const start = i * batchSize;
const end = Math.min((i + 1) * batchSize, submissionIds.length);
const currentBatch = submissionIds.slice(start, end);
console.log(`Processing batch ${i + 1}/${totalBatches} (${currentBatch.length} submissions)...`);
try {
// DANGER ZONE: Uncomment to delete submissions
// await api.deleteSubmissions(workspaceId, currentBatch);
// console.log(`Successfully deleted batch ${i + 1}/${totalBatches} (${currentBatch.length} submissions).`);
// Safe output
console.log(`Would delete batch ${i + 1}/${totalBatches} (${currentBatch.length} submissions).`);
// Check if we need to wait before processing the next batch (except for the last batch)
if (i < totalBatches - 1) {
console.log(`Waiting ${delayMs}ms before next batch to respect rate limits...`);
await delay(delayMs);
}
} catch (error) {
if (error instanceof Error && error.message.includes('429')) {
// Rate limit exceeded
const retryAfter = 5000; // Default to 5 seconds if no header provided
console.log(`Rate limit exceeded. Waiting ${retryAfter}ms before retrying...`);
await delay(retryAfter);
i--; // Retry this batch
} else {
// Other error, re-throw
throw error;
}
}
}
console.log(`Batch processing complete for ${submissionIds.length} submissions.`);
}
// Usage example with rate limit handling
// await batchDeleteWithRateLimit(api, "workspace-id", submissionIds, 50, 2000);
Integration Example with Rate Limit Handling
Here's how to integrate rate limit handling into a complete solution: Example only in typescript, change tab to typescript
// Integration Example with Rate Limit Handling
// Enhanced API client with rate limit handling
class EnhancedInstapageAPI extends InstapageAPI {
private rateLimitDelay: number;
constructor(token: string, rateLimitDelay: number = 1000) {
super(token);
this.rateLimitDelay = rateLimitDelay;
}
// Helper method to wait between requests
private async delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Method to get all submissions with pagination
async getAllSubmissionsFromRange(
workspaceId: string,
pageIds: string[],
startDate: string,
endDate: string
): Promise<Submission[]> {
let allSubmissions: Submission[] = [];
let nextPageToken: string | null = null;
do {
try {
const data = {
pages: pageIds,
timeframe: {
start: Math.floor(new Date(startDate).getTime() / 1000),
end: Math.floor(new Date(endDate).getTime() / 1000)
},
nextPageToken: nextPageToken
};
const response = await this.request(`/workspaces/${workspaceId}/submissions`, 'POST', data);
// Add submissions from current page to our collection
if (response.data.submissions && response.data.submissions.length > 0) {
allSubmissions = [...allSubmissions, ...response.data.submissions];
}
// Get the next page token for pagination
nextPageToken = response.meta?.nextPageToken || null;
if (nextPageToken) {
console.log(`Fetching next page of submissions with token: ${nextPageToken}`);
// Add a small delay between pagination requests to avoid rate limits
await this.delay(this.rateLimitDelay / 2);
}
} catch (error) {
// Check if it's a rate limit error
if (error instanceof Error && error.message.includes('429')) {
console.log(`Rate limit exceeded while fetching submissions. Waiting before retrying...`);
await this.delay(5000); // Wait longer for rate limit errors
// Don't update nextPageToken, so we retry the same page
} else {
throw error; // Re-throw other errors
}
}
} while (nextPageToken);
return allSubmissions;
}
// Override the deleteSubmissions method to handle rate limits
async deleteSubmissions(workspaceId: string, submissionIds: string[]): Promise<any> {
try {
// If the batch is too large, process in smaller batches
const MAX_BATCH_SIZE = 100;
if (submissionIds.length > MAX_BATCH_SIZE) {
console.log(`Large batch detected (${submissionIds.length} submissions). Processing in smaller batches...`);
const results = [];
for (let i = 0; i < submissionIds.length; i += MAX_BATCH_SIZE) {
const batch = submissionIds.slice(i, i + MAX_BATCH_SIZE);
console.log(`Processing batch ${Math.floor(i/MAX_BATCH_SIZE) + 1}/${Math.ceil(submissionIds.length/MAX_BATCH_SIZE)} (${batch.length} submissions)...`);
// Make the API call
const result = await super.deleteSubmissions(workspaceId, batch);
results.push(result);
// Wait before the next batch (unless it's the last one)
if (i + MAX_BATCH_SIZE < submissionIds.length) {
console.log(`Waiting ${this.rateLimitDelay}ms before next batch...`);
await this.delay(this.rateLimitDelay);
}
}
return results;
}
// For smaller batches, just make the normal call
return await super.deleteSubmissions(workspaceId, submissionIds);
} catch (error) {
// Check if it's a rate limit error
if (error instanceof Error && error.message.includes('429')) {
console.log(`Rate limit exceeded. Waiting before retrying...`);
await this.delay(5000); // Wait longer for rate limit errors
return this.deleteSubmissions(workspaceId, submissionIds); // Retry
}
throw error; // Re-throw other errors
}
}
}
async function main() {
const token = ''; // Replace with your actual API token
// Create enhanced API client with rate limit handling (wait 2 seconds between batches)
const api = new EnhancedInstapageAPI(token, 2000);
const workspaceId = ''; // Replace with your workspace ID
try {
// Example 1: Delete submissions from year 2024 for a specific page
const pageId1 = ''; // Replace with your page ID
await removeSubmissionsFromYear(api, workspaceId, pageId1, 2024);
// Example 2: Delete submissions from January 2025 for a specific page
const pageId2 = ''; // Replace with your page ID
await removeSubmissionsFromPeriod(api, workspaceId, pageId2, "2025-01-01", "2025-01-31");
// Example 3: Delete submissions older than March 1, 2025 for a specific page
const pageId3 = ''; // Replace with your page ID
await removeSubmissionsOlderThan(api, workspaceId, pageId3, "2025-03-01");
// Example 4: Using the standalone batch deletion function with pagination
// Get all submissions from a specific page
const pageId4 = ''; // Replace with your page ID
console.log(`Fetching all submissions for page ${pageId4}...`);
const allSubmissions = await api.getAllSubmissionsFromRange(
workspaceId,
[pageId4],
"2000-01-01", // Start from a very early date
new Date().toISOString().split('T')[0] // Today
);
if (allSubmissions.length > 0) {
console.log(`Found ${allSubmissions.length} total submissions across all pages for page ${pageId4}`);
const allSubmissionIds = allSubmissions.map(s => s.id);
// Use the batch function with custom batch size (50) and delay (3 seconds)
await batchDeleteWithRateLimit(api, workspaceId, allSubmissionIds, 50, 3000);
} else {
console.log(`No submissions found for page ${pageId4}`);
}
} catch (error) {
console.error('An error occurred:', error instanceof Error ? error.message : String(error));
}
}