Getting Started with the InLoox API
This guide walks you through everything you need to make your first API request — from authentication to querying data with OData.
Prerequisites
Before you begin, make sure you have:
- An InLoox account (Cloud or Self-Hosted with API access enabled)
- A Personal Access Token — you can generate one from your InLoox profile settings under Options → Personal Access Token
- An HTTP client such as cURL, Postman, or your programming language of choice
Navigate in InLoox to File → Options → Personal Access Tokens and click Create New Key. Copy the generated token — it is only shown once.
Your Personal Access Token grants full access to the API on your behalf. Treat it like a password — never commit it to source control or share it publicly.
Authentication
The InLoox API uses token-based authentication via the x-api-key HTTP header. Include this header in every request.
cURL
curl -X GET "https://app.inloox.com/api/odata/Project" \
-H "x-api-key: YOUR_API_TOKEN"
C# (HttpClient)
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_TOKEN");
var response = await client.GetAsync("https://app.inloox.com/api/odata/Project");
var json = await response.Content.ReadAsStringAsync();
Console.WriteLine(json);
C# (Simple.OData.Client)
var settings = new ODataClientSettings(new Uri("https://app.inloox.com/api/odata/"));
settings.BeforeRequest += delegate (HttpRequestMessage message)
{
message.Headers.Add("x-api-key", "YOUR_API_TOKEN");
};
var client = new ODataClient(settings);
JavaScript (Fetch)
const response = await fetch("https://app.inloox.com/api/odata/Project", {
headers: {
"x-api-key": "YOUR_API_TOKEN"
}
});
const data = await response.json();
console.log(data);
A quick way to verify your token works is to call the AccountInfo endpoint:
curl -X GET "https://app.inloox.com/api/odata/AccountInfo" \
-H "x-api-key: YOUR_API_TOKEN"
If authentication succeeds, you'll receive your user profile information.
Base URL
All API requests are made against a base URL that depends on your deployment:
| Deployment | Base URL |
|---|---|
| InLoox Cloud | https://app.inloox.com/api/odata/ |
| InLoox Self-Hosted | https://YOUR-SELF-HOSTED-URL/api/v1/odata/ |
All endpoint paths in this documentation are relative to the base URL. For example:
GET /api/odata/Project
means the full URL for Cloud is https://app.inloox.com/api/odata/Project.
OData Basics
The InLoox API follows the OData (Open Data Protocol) standard. OData provides a uniform way to query and manipulate data using standard HTTP methods and URL conventions.
What is OData?
OData is an open standard for building and consuming REST APIs. It defines proven patterns for queries controlled via URL parameters ($filter, $select, $orderby, etc.). This lets you precisely control which data you receive without requiring custom endpoints.
Supported HTTP Methods
| Method | Usage |
|---|---|
GET | Read entities or collections |
POST | Create new entities or invoke actions |
PATCH | Update existing entities (partial update) |
DELETE | Delete entities |
Query Options
OData supports powerful query options as URL parameters. These let you filter, sort, page, and shape the data returned by the API.
$filter — Filter Results
Use $filter to restrict the results to entities that match a condition.
Operators: eq, ne, gt, ge, lt, le, and, or, not
# Projects named "Website Redesign"
curl "https://app.inloox.com/api/odata/Project?\$filter=Name eq 'Website Redesign'" \
-H "x-api-key: YOUR_API_TOKEN"
# Projects started after January 1, 2024
curl "https://app.inloox.com/api/odata/Project?\$filter=StartDate gt 2024-01-01" \
-H "x-api-key: YOUR_API_TOKEN"
# Open tasks in a specific project
curl "https://app.inloox.com/api/odata/Task?\$filter=ProjectId eq {projectId} and IsDone eq false" \
-H "x-api-key: YOUR_API_TOKEN"
// C# with Simple.OData.Client
var projects = await client
.For<ApiProject>("Project")
.Filter(p => p.Name == "Website Redesign")
.FindEntriesAsync();
String values must be enclosed in single quotes: Name eq 'My Project'. GUIDs and dates do not need quotes.
Common filter operators:
| Operator | Meaning | Example |
|---|---|---|
eq | Equals | Name eq 'Test' |
ne | Not equals | Status ne 'Closed' |
gt | Greater than | CreatedDate gt 2024-01-01T00:00:00Z |
lt | Less than | Progress lt 50 |
ge | Greater than or equal | Progress ge 80 |
le | Less than or equal | Progress le 100 |
and | Logical and | IsActive eq true and Progress gt 0 |
or | Logical or | Status eq 'Open' or Status eq 'InProgress' |
contains() | Contains | contains(Name, 'Marketing') |
startswith() | Starts with | startswith(Name, 'Project') |
$select — Choose Specific Properties
Use $select to return only the properties you need. This reduces response size and improves performance.
# Return only project ID and name
curl "https://app.inloox.com/api/odata/Project?\$select=ProjectId,Name" \
-H "x-api-key: YOUR_API_TOKEN"
// C# with Simple.OData.Client
var projects = await client
.For<ApiProject>("Project")
.Select(p => new { p.ProjectId, p.Name })
.FindEntriesAsync();
$orderby — Sort Results
Use $orderby to sort results by one or more properties. Append asc (default) or desc for sort direction.
# Projects sorted by name ascending
curl "https://app.inloox.com/api/odata/Project?\$orderby=Name asc" \
-H "x-api-key: YOUR_API_TOKEN"
# Projects sorted by start date descending
curl "https://app.inloox.com/api/odata/Project?\$orderby=StartDate desc" \
-H "x-api-key: YOUR_API_TOKEN"
// C# with Simple.OData.Client
var projects = await client
.For<ApiProject>("Project")
.OrderBy(p => p.StartDate)
.FindEntriesAsync();
$top and $skip — Paging
Use $top to limit the number of results and $skip to offset into the collection. Together, they enable paging.
# First 10 projects
curl "https://app.inloox.com/api/odata/Project?\$top=10" \
-H "x-api-key: YOUR_API_TOKEN"
# Projects 11–20 (page 2 with page size 10)
curl "https://app.inloox.com/api/odata/Project?\$top=10&\$skip=10" \
-H "x-api-key: YOUR_API_TOKEN"
// C# with Simple.OData.Client
var page2 = await client
.For<ApiProject>("Project")
.Skip(10)
.Top(10)
.FindEntriesAsync();
$count — Get Total Count
Use $count to get the total number of entities that match your query.
# Total number of projects
curl "https://app.inloox.com/api/odata/Project/\$count" \
-H "x-api-key: YOUR_API_TOKEN"
You can also combine $count=true with a regular query to include the count in the response alongside the data:
curl "https://app.inloox.com/api/odata/Project?\$count=true&\$top=10" \
-H "x-api-key: YOUR_API_TOKEN"
$expand — Include Related Entities
Where supported, use $expand to include related entities inline rather than making separate requests.
curl "https://app.inloox.com/api/odata/Task?\$expand=Project" \
-H "x-api-key: YOUR_API_TOKEN"
Not all entities support $expand. Check the specific entity documentation for supported expansions.
Combining Query Options
You can combine multiple query options in a single request:
# Top 5 open tasks in a project, sorted by due date, returning only key fields
curl "https://app.inloox.com/api/odata/Task?\$filter=ProjectId eq {projectId} and IsDone eq false&\$orderby=EndDate asc&\$top=5&\$select=TaskItemId,DisplayName,EndDate" \
-H "x-api-key: YOUR_API_TOKEN"
Paging
The API returns a maximum of 100 items per request by default. If your query matches more than 100 items, you must paginate through the results.
Paging Strategy
- Send a request with
$topand optionally$count=true - Check if the response contains an
@odata.nextLinkfield - If yes, send the next request to the URL specified in
@odata.nextLink - Repeat until no
@odata.nextLinkis present
How Paging Works
When there are more results available, the response includes a @odata.nextLink property containing the URL for the next page:
{
"@odata.context": "https://app.inloox.com/api/odata/$metadata#Project",
"value": [ ... ],
"@odata.nextLink": "https://app.inloox.com/api/odata/Project?$skip=100"
}
Manual Paging with $top and $skip
# Page 1 (items 1–100)
curl "https://app.inloox.com/api/odata/Project?\$top=100&\$skip=0" \
-H "x-api-key: YOUR_API_TOKEN"
# Page 2 (items 101–200)
curl "https://app.inloox.com/api/odata/Project?\$top=100&\$skip=100" \
-H "x-api-key: YOUR_API_TOKEN"
# Page 3 (items 201–300)
curl "https://app.inloox.com/api/odata/Project?\$top=100&\$skip=200" \
-H "x-api-key: YOUR_API_TOKEN"
Manual Paging Example (C#)
var allProjects = new List<ApiProject>();
int skip = 0;
const int pageSize = 100;
while (true)
{
var response = await client.GetAsync($"Project?$top={pageSize}&$skip={skip}");
var json = await response.Content.ReadAsStringAsync();
var page = JsonSerializer.Deserialize<ODataResponse<ApiProject>>(json);
allProjects.AddRange(page.Value);
if (page.Value.Count < pageSize)
break;
skip += pageSize;
}
Console.WriteLine($"Total {allProjects.Count} projects loaded.");
Paging with Simple.OData.Client (C#)
The ODataFeedAnnotations class handles paging automatically:
var allEntries = new List<ApiProject>();
var annotations = new ODataFeedAnnotations();
var page = (await client
.For<ApiProject>("Project")
.FindEntriesAsync(annotations)).ToList();
allEntries.AddRange(page);
while (annotations.NextPageLink != null)
{
page = (await client
.For<ApiProject>("Project")
.FindEntriesAsync(annotations.NextPageLink, annotations)).ToList();
allEntries.AddRange(page);
}
Console.WriteLine($"Total projects loaded: {allEntries.Count}");
When fetching large datasets, consider using $filter to narrow your query rather than paginating through all records. This reduces both network traffic and server load.
Response Format
All API responses use JSON with OData metadata annotations. A typical collection response looks like:
{
"@odata.context": "https://app.inloox.com/api/odata/$metadata#Project",
"value": [
{
"ProjectId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"Name": "Website Redesign",
"StartDate": "2024-01-15T00:00:00Z",
"EndDate": "2024-06-30T00:00:00Z",
"IsClosed": false
}
]
}
Key elements:
| Property | Description |
|---|---|
@odata.context | URL to the entity metadata schema |
value | Array of entities (for collections) |
@odata.nextLink | URL of the next page (if more data is available) |
@odata.count | Total count of matching entities (when $count=true) |
A single-entity response (e.g., GET Project({key})) returns the entity directly without the value wrapper:
{
"@odata.context": "https://app.inloox.com/api/odata/$metadata#Project/$entity",
"ProjectId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"Name": "Website Redesign",
"StartDate": "2024-01-15T00:00:00Z",
"EndDate": "2024-06-30T00:00:00Z",
"IsClosed": false
}
Error Handling
The API uses standard HTTP status codes. Handle these in your integration:
| Status Code | Meaning | Typical Cause |
|---|---|---|
200 OK | Success | Request processed successfully |
201 Created | Created | Entity created via POST |
204 No Content | Success | Update or delete completed |
400 Bad Request | Client error | Malformed request, invalid filter syntax, or missing required fields |
401 Unauthorized | Authentication failed | Missing or invalid x-api-key header |
403 Forbidden | Access denied | Token is valid but user lacks permission for this operation |
404 Not Found | Resource not found | Entity ID doesn't exist or endpoint is incorrect |
429 Too Many Requests | Rate limited | Too many requests in a short period |
500 Internal Server Error | Server error | Unexpected server-side issue — contact InLoox support |
Error Response Format
Error responses include a JSON body with details:
{
"error": {
"code": "400",
"message": "The query specified in the URI is not valid. Syntax error at position 15."
}
}
- Double-check your
$filtersyntax — OData filter expressions are strict about quoting and operator usage. - Verify that property names in
$select,$filter, and$orderbyare case-sensitive and match the API schema. - Use the
$metadataendpoint to inspect available entities and properties:GET https://app.inloox.com/api/odata/$metadata
Rate Limits
While the InLoox API does not publish strict rate limits, follow these best practices to avoid throttling:
- Batch your reads — use
$filterand$selectto minimize the number of requests - Don't poll aggressively — wait at least a few seconds between repeated requests
- Paginate efficiently — follow
@odata.nextLinkinstead of making overlapping queries - Cache when appropriate — store results locally if the data doesn't change frequently
If you receive a 429 Too Many Requests response, wait for the duration indicated in the Retry-After header before retrying.
Next Steps
You're ready to start building! Here's where to go next:
- Code Examples — Complete working C# examples you can clone and run
- Projects — Learn to work with the
Projectentity - Tasks — Query and manage tasks
- Time Entries — Record and retrieve time tracking data