List one level of a project's file browser
files:readThe 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.
Path parameters
project_idstringrequiredThe project SLUG.
Query parameters
folder_idintegerThe folder to list inside; omit for the project root.
limitintegerPage size (default 50, max 100, clamped).
Default: 50
offsetintegerNumber of records to skip (default 0).
Default: 0
Request
curl "https://app.fareeqy.com/api/v1/projects/تطوير-الموقع/files" \
-H "Authorization: Bearer $FAREEQY_API_KEY"Response
{
"data": {
"folder": {
"id": 42,
"name": "التصاميم",
"path": "التصاميم",
"description": "ملفات الهوية وواجهات الموقع.",
"parent_folder_id": null,
"created_at": "2026-07-05T08:20:00.000+03:00"
},
"folders": [
{
"id": 51,
"name": "الأيقونات",
"path": "التصاميم/الأيقونات",
"description": null,
"parent_folder_id": 42,
"created_at": "2026-07-06T09:02:14.000+03:00"
}
],
"files": [
{
"id": 108,
"name": "الهوية-البصرية.pdf",
"path": "التصاميم/الهوية-البصرية.pdf",
"folder_id": 42,
"content_type": "application/pdf",
"size_bytes": 2418176,
"size_human": "2.31 م.بايت",
"external_url": null,
"source": null,
"scan_state": "clean",
"downloadable": true,
"uploaded_by": "سارة العتيبي",
"created_at": "2026-07-10T16:31:44.000+03:00"
},
{
"id": 131,
"name": "واجهات الصفحة الرئيسية",
"path": "التصاميم/واجهات الصفحة الرئيسية",
"folder_id": 42,
"content_type": null,
"size_bytes": null,
"size_human": null,
"external_url": "https://www.figma.com/design/9aZq/home-v3",
"source": "Figma",
"scan_state": "skipped",
"downloadable": true,
"uploaded_by": "سارة العتيبي",
"created_at": "2026-07-12T11:04:20.000+03:00"
},
{
"id": 147,
"name": "لقطة-الصفحة-الرئيسية.png",
"path": "التصاميم/لقطة-الصفحة-الرئيسية.png",
"folder_id": 42,
"content_type": "image/png",
"size_bytes": 884736,
"size_human": "864 ك.بايت",
"external_url": null,
"source": null,
"scan_state": "clean",
"downloadable": true,
"uploaded_by": "عبدالله المطيري",
"created_at": "2026-07-14T10:22:07.000+03:00"
},
{
"id": 152,
"name": "عرض-التدفق-الجديد.mp4",
"path": "التصاميم/عرض-التدفق-الجديد.mp4",
"folder_id": 42,
"content_type": "video/mp4",
"size_bytes": 41943040,
"size_human": "40 م.بايت",
"external_url": null,
"source": null,
"scan_state": "pending",
"downloadable": false,
"uploaded_by": "نورة الحربي",
"created_at": "2026-07-15T13:47:58.000+03:00"
}
]
},
"meta": {
"total": 4,
"limit": 50,
"offset": 0
}
}Refusals:401403404429Show failure examplesHide failure examples
defaultMissing or invalid API key.
{
"error": {
"code": "unauthorized",
"message": "Invalid or missing API key."
}
}forbiddenTwo 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.
{
"error": {
"code": "forbidden",
"message": "This API key is not permitted to perform this operation."
}
}{
"error": {
"code": "plan_upgrade_required",
"message": "خطة «الاحترافي» لا تشمل الوصول إلى API. رقِّ إلى «المتطور» أو «الانتاجي» لتفعيله. — The الاحترافي plan does not include API access. Upgrade to «المتطور» or «الانتاجي» to enable it."
}
}defaultResource 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.
{
"error": {
"code": "not_found",
"message": "Resource not found, or you do not have access to it."
}
}rate_limit_exceededEither 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.
{
"error": {
"code": "rate_limit_exceeded",
"message": "استهلكت رصيد اليوم من طلبات API في خطة «المتطور» (1000 طلب يوميًا). يتجدد الرصيد عند منتصف الليل بتوقيت Asia/Riyadh. — Daily API quota exhausted: the المتطور plan allows 1000 calls per day. It resets at midnight Asia/Riyadh."
}
}{
"error": "Rate limit exceeded. Please try again later."
}