---
title: "List one level of a project's file browser"
method: GET
path: "/projects/{project_id}/files"
tags: ["Files"]
---

# List one level of a project's file browser

`GET /projects/{project_id}/files`

## Request

- Base URL: `https://app.fareeqy.com/api/v1`
- URL: `https://app.fareeqy.com/api/v1/projects/{project_id}/files`
- Auth: HTTP bearer

The folders and files directly inside `folder_id` (or the project root).
Not recursive — walk down by passing a subfolder's id back. Files are
paginated; folders are not. Requires operation `files:read`.

## Query parameters

- `folder_id` integer
- `limit` integer
- `offset` integer

## Response `200`

One level of the file browser.

- object
  - `data` object
    - `folder` Folder
      - `id` integer: Numeric id (folders have no slug).
      - `url` string, nullable: The project's files page scoped to this folder.
      - `name` string
      - `path` string
      - `description` string, nullable
      - `parent_folder_id` integer, nullable
      - `created_at` string, date-time
    - `folders` Folder[]
      - `id` integer: Numeric id (folders have no slug).
      - `url` string, nullable: The project's files page scoped to this folder.
      - `name` string
      - `path` string
      - `description` string, nullable
      - `parent_folder_id` integer, nullable
      - `created_at` string, date-time
    - `files` ProjectFile[]
      - `id` integer: Numeric id (files have no slug).
      - `url` string, nullable: Absolute address of the page a person opens in فريقي.
      - `name` string
      - `path` string: The folder path the file lives at.
      - `folder_id` integer, nullable
      - `content_type` string, nullable
      - `size_bytes` integer, nullable
      - `size_human` string, nullable
      - `external_url` string, nullable: Set for an external link, null for an upload.
      - `source` string, nullable: The source's display name.
      - `scan_state` string: Virus-scan state (e.g. pending, clean).
      - `downloadable` boolean: True once the scan has cleared the file.
      - `uploaded_by` string, nullable
      - `created_at` string, date-time
  - `meta` object
    - `total` integer: Total files in this folder (folders are not paginated).
    - `limit` integer
    - `offset` integer

## Other responses

- `401`: Missing or invalid API key.
- `403`: Two different refusals share this status, and a client must tell them apart by `error.code`. `forbidden` means the key's scope or allowlist does not permit this operation, or Pundit denied the action. `plan_upgrade_required` means the company's plan carries no API access at all, so no key on it can ever succeed and there is nothing to retry.
- `404`: Resource not found or not accessible — also returned for EVERY endpoint when the company's `rest_api` feature flag is disabled (the surface is hidden). Lookups drill through the URL hierarchy, so another company's record is a 404 and never a leak. A path that matches no route at all answers 404 with the distinct code `unknown_endpoint` and echoes the path back, so a mistyped or half-built URL is told apart from a record that is missing or out of reach.
- `429`: Either the company's daily API allowance is spent (`rate_limit_exceeded`), or the per-key / per-IP burst throttle of 300 requests per minute fired. Both come back after a wait, so `Retry-After` is honest here. **The two bodies are not the same shape.** The daily-quota refusal uses the standard error envelope. The burst throttle is served by Rack::Attack ahead of the application, so its body is a flat `{"error": "<string>"}` with no `code`. A client that reads `error.code` has to tolerate `error` being a plain string.

---

[API](https://skmtc.dev/fareeqy/apis/fareeqy-rest-api.md) · [All operations](https://skmtc.dev/fareeqy/apis/fareeqy-rest-api/llms.txt) · [OpenAPI document](https://skmtc.dev/fareeqy/apis/fareeqy-rest-api/revisions/dc964e05e3d2?raw)
