> ## Documentation Index
> Fetch the complete documentation index at: https://docs.directify.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Listings

> Manage listings through the Directify API - create, read, update, and delete listings with full CRUD operations.

## Listings

In Directify, **listings** are the items that appear in your directory. These can be tools, services, products, or any other items you want to showcase. The API endpoints use "projects" in the URL, but they refer to listings in your directory.

### Get Directory Listings

Retrieve all listings for a specific directory with pagination.

```http theme={null}
GET /api/directories/{directory_id}/projects
```

**Parameters:**

* `directory_id` (integer, required): The ID of the directory
* `page` (integer, optional): Page number for pagination (default: 1)

**Response:**

```json theme={null}
{
  "data": [
    {
      "id": 1,
      "name": "React.js",
      "slug": "react-js",
      "description": "A JavaScript library for building user interfaces",
      "content": "Full content body with HTML...",
      "url": "https://reactjs.org",
      "image_url": "https://example.com/images/react.jpg",
      "logo_url": "https://example.com/logos/react.png",
      "logo_alt_text": "React.js Logo",
      "image_alt_text": "React.js Homepage",
      "additional_image_urls": ["https://example.com/gallery1.jpg", "https://example.com/gallery2.jpg"],
      "additional_images_urls": ["https://cdn.example.com/gallery1.jpg", "https://cdn.example.com/gallery2.jpg"],
      "video_url": null,
      "video_thumbnail_url": null,
      "phone_number": "+1 (555) 123-4567",
      "email": "contact@reactjs.org",
      "address": "1 Hacker Way, Menlo Park, CA",
      "latitude": 37.4847,
      "longitude": -122.1477,
      "social_links": {
        "twitter": "https://twitter.com/reactjs",
        "github": "https://github.com/facebook/react"
      },
      "categories": [
        {
          "id": 1,
          "title": "Frontend",
          "parent_title": "Web Development"
        }
      ],
      "tags": [
        {
          "id": 1,
          "title": "Open Source",
          "slug": "open-source",
          "color": "#3B82F6",
          "text_color": "#FFFFFF",
          "is_active": true
        }
      ],
      "organizers": [
        { "id": 5, "name": "Acme Events", "slug": "acme-events" }
      ],
      "is_active": true,
      "is_featured": false,
      "is_no_follow": false,
      "is_claimed": false,
      "votes": 42,
      "seo_title": "React.js - A JavaScript Library for Building User Interfaces",
      "seo_description": "React makes it painless to create interactive UIs.",
      "og_image": null,
      "schema": null,
      "head_html": null,
      "created_at": "2024-01-15T10:30:00.000000Z",
      "updated_at": "2024-01-15T10:30:00.000000Z",
      "listing_url": "https://your-directify-domain.com/app/1/projects/1/edit",
      "pricing": "$0 - $100/month",
      "job_type": "Full-time"
    }
  ],
  "links": {
    "first": "https://your-directify-domain.com/api/directories/1/projects?page=1",
    "last": "https://your-directify-domain.com/api/directories/1/projects?page=5",
    "prev": null,
    "next": "https://your-directify-domain.com/api/directories/1/projects?page=2"
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 5,
    "per_page": 100,
    "to": 100,
    "total": 450
  }
}
```

### Get Single Listing

Retrieve a specific listing by ID.

```http theme={null}
GET /api/directories/{directory_id}/projects/{project_id}
```

**Parameters:**

* `directory_id` (integer, required): The ID of the directory
* `project_id` (integer, required): The ID of the listing

**Response:**

```json theme={null}
{
  "data": {
    "id": 1,
    "name": "React.js",
    "slug": "react-js",
    "description": "A JavaScript library for building user interfaces",
    "content": "Full content body with HTML...",
    "url": "https://reactjs.org",
    "image_url": "https://example.com/images/react.jpg",
    "logo_url": "https://example.com/logos/react.png",
    "logo_alt_text": "React.js Logo",
    "image_alt_text": "React.js Homepage",
    "additional_image_urls": ["https://example.com/gallery1.jpg"],
    "additional_images_urls": ["https://cdn.example.com/gallery1.jpg"],
    "video_url": null,
    "video_thumbnail_url": null,
    "phone_number": "+1 (555) 123-4567",
    "email": "contact@reactjs.org",
    "address": "1 Hacker Way, Menlo Park, CA",
    "latitude": 37.4847,
    "longitude": -122.1477,
    "social_links": {
      "twitter": "https://twitter.com/reactjs",
      "github": "https://github.com/facebook/react"
    },
    "categories": [
      {
        "id": 1,
        "title": "Frontend",
        "parent_title": "Web Development"
      }
    ],
    "tags": [
      {
        "id": 1,
        "title": "Open Source",
        "slug": "open-source",
        "color": "#3B82F6",
        "text_color": "#FFFFFF",
        "is_active": true
      }
    ],
    "organizers": [
      { "id": 5, "name": "Acme Events", "slug": "acme-events" }
    ],
    "is_active": true,
    "is_featured": false,
    "is_no_follow": false,
    "is_claimed": false,
    "votes": 42,
    "seo_title": "React.js - A JavaScript Library for Building User Interfaces",
    "seo_description": "React makes it painless to create interactive UIs.",
    "og_image": null,
    "schema": null,
    "head_html": null,
    "created_at": "2024-01-15T10:30:00.000000Z",
    "updated_at": "2024-01-15T10:30:00.000000Z",
    "listing_url": "https://your-directify-domain.com/app/1/projects/1/edit",
    "pricing": "$0 - $100/month",
    "job_type": "Full-time"
  }
}
```

### Check Listing Exists

Check if a listing with a specific URL already exists in the directory.

```http theme={null}
POST /api/directories/{directory_id}/projects/exists
```

**Parameters:**

* `directory_id` (integer, required): The ID of the directory

**Request Body:**

```json theme={null}
{
  "url": "https://example.com"
}
```

**Response (Listing exists):**

```json theme={null}
{
  "message": "Listing already exists in this directory"
}
```

**Response (Listing doesn't exist):**

```json theme={null}
{
  "message": "Listing does not exist in this directory"
}
```

### Create Listing

Create a new listing in the directory.

```http theme={null}
POST /api/directories/{directory_id}/projects
```

**Parameters:**

* `directory_id` (integer, required): The ID of the directory

**Request Body:**

```json theme={null}
{
  "name": "Example Tool",
  "url": "https://example.com",
  "description": "A great tool for developers",
  "content": "<p>Full HTML content for the listing page...</p>",
  "phone_number": "+1 (555) 123-4567",
  "email": "contact@example.com",
  "address": "123 Main St, New York, NY 10001",
  "latitude": 40.7128,
  "longitude": -74.0060,
  "social_links": {
    "twitter": "https://twitter.com/example",
    "linkedin": "https://linkedin.com/company/example"
  },
  "category_id": 1,
  "categories": [1, 2],
  "tags": [1, 3],
  "organizers": [5, 8],
  "image_url": "https://example.com/cover.jpg",
  "logo_url": "https://example.com/logo.png",
  "additional_image_urls": [
    "https://example.com/gallery1.jpg",
    "https://example.com/gallery2.jpg"
  ],
  "video_url": "https://youtube.com/embed/abc123",
  "video_thumbnail_url": "https://example.com/video-thumb.jpg",
  "starts_at": "2026-08-01 09:00:00",
  "ends_at": "2026-09-01 09:00:00",
  "seo_title": "Example Tool - Best Developer Tool",
  "seo_description": "The leading tool for developers worldwide.",
  "head_html": "<link rel=\"alternate\" hreflang=\"es\" href=\"https://es.example.com/listings/example-tool\">",
  "is_active": true,
  "is_featured": false,
  "is_no_follow": false,
  "pricing": "Free",
  "job_type": "Full-time"
}
```

<Note>
  **Linking organizers:** Pass `organizers` as an array of organizer IDs to associate the listing with one or more [organizers](/api-integration/organizers) (agencies, studios, event hosts, etc.). Only organizers belonging to the same directory are linked — any IDs from another directory are silently ignored. Fetch available IDs with `GET /api/directories/{directory_id}/organizers`.
</Note>

<Note>
  **Expiring listings:** `starts_at` and `ends_at` control when a listing is visible. A listing with a `starts_at` in the future stays hidden until that moment, and once `ends_at` passes it is either badged as expired or hidden entirely — depending on the directory's [expiring listings setting](/listings/add-listing#expiring-listings). Both are optional; omit them and the listing is live immediately and never expires. Accepts any date format PHP can parse, e.g. `2026-08-01 09:00:00` or `2026-08-01T09:00:00Z`.
</Note>

**Response:**

```json theme={null}
{
  "data": {
    "id": 123,
    "name": "Example Tool",
    "slug": "example-tool",
    "description": "A great tool for developers",
    "content": "<p>Full HTML content for the listing page...</p>",
    "url": "https://example.com",
    "image_url": "https://example.com/cover.jpg",
    "logo_url": "https://example.com/logo.png",
    "logo_alt_text": null,
    "image_alt_text": null,
    "additional_image_urls": ["https://example.com/gallery1.jpg", "https://example.com/gallery2.jpg"],
    "additional_images_urls": ["https://example.com/gallery1.jpg", "https://example.com/gallery2.jpg"],
    "video_url": null,
    "video_thumbnail_url": null,
    "phone_number": "+1 (555) 123-4567",
    "email": "contact@example.com",
    "address": "123 Main St, New York, NY 10001",
    "latitude": 40.7128,
    "longitude": -74.006,
    "social_links": {
      "twitter": "https://twitter.com/example",
      "linkedin": "https://linkedin.com/company/example"
    },
    "categories": [
      {
        "id": 1,
        "title": "Web Development",
        "parent_title": null
      },
      {
        "id": 2,
        "title": "Frontend",
        "parent_title": "Web Development"
      }
    ],
    "tags": [
      {
        "id": 1,
        "title": "Open Source",
        "slug": "open-source",
        "color": "#3B82F6",
        "text_color": "#FFFFFF",
        "is_active": true
      },
      {
        "id": 3,
        "title": "Free Tier",
        "slug": "free-tier",
        "color": "#10B981",
        "text_color": "#FFFFFF",
        "is_active": true
      }
    ],
    "organizers": [
      { "id": 5, "name": "Acme Events", "slug": "acme-events" },
      { "id": 8, "name": "Downtown Collective", "slug": "downtown-collective" }
    ],
    "is_active": true,
    "is_featured": false,
    "is_no_follow": false,
    "is_claimed": false,
    "votes": 0,
    "seo_title": "Example Tool - Best Developer Tool",
    "seo_description": "The leading tool for developers worldwide.",
    "og_image": null,
    "schema": null,
    "head_html": null,
    "created_at": "2024-01-15T10:30:00.000000Z",
    "updated_at": "2024-01-15T10:30:00.000000Z",
    "listing_url": "https://your-directify-domain.com/app/1/projects/123/edit",
    "pricing": "Free",
    "job_type": "Full-time"
  }
}
```

### Bulk Create Listings

Create multiple listings in a single request. Up to 100 listings can be created at once. Each listing is processed independently, so if one fails, the others will still be created.

```http theme={null}
POST /api/directories/{directory_id}/projects/bulk
```

**Parameters:**

* `directory_id` (integer, required): The ID of the directory

**Request Body:**

```json theme={null}
{
  "listings": [
    {
      "name": "Listing 1",
      "description": "Description for listing 1",
      "url": "https://example1.com",
      "categories": [1, 2],
      "tags": [1],
      "organizers": [5],
      "is_active": true
    },
    {
      "name": "Listing 2",
      "description": "Description for listing 2",
      "category_id": 3,
      "is_featured": true
    },
    {
      "name": "Listing 3",
      "description": "Description for listing 3",
      "url": "https://example3.com",
      "email": "contact@example3.com",
      "phone_number": "+1234567890",
      "address": "123 Main St"
    }
  ]
}
```

**Available fields per listing:**

| Field                   | Type             | Description                                                                                                  |
| ----------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------ |
| `name`                  | string, required | The listing name                                                                                             |
| `slug`                  | string           | URL slug (auto-generated from name if omitted)                                                               |
| `description`           | string           | Short description                                                                                            |
| `content`               | string           | Full content/body (HTML supported)                                                                           |
| `url`                   | string           | Website URL (used for duplicate detection)                                                                   |
| `phone_number`          | string           | Phone number                                                                                                 |
| `email`                 | string           | Contact email                                                                                                |
| `address`               | string           | Physical address                                                                                             |
| `latitude`              | float            | Map latitude coordinate                                                                                      |
| `longitude`             | float            | Map longitude coordinate                                                                                     |
| `social_links`          | object           | Social media links (e.g., `{"twitter": "...", "linkedin": "..."}`)                                           |
| `category_id`           | integer          | Single category ID                                                                                           |
| `categories`            | array            | Array of category IDs                                                                                        |
| `tags`                  | array            | Array of tag IDs                                                                                             |
| `organizers`            | array            | Array of organizer IDs to link (must belong to the same directory; out-of-directory IDs are ignored)         |
| `is_active`             | boolean          | Published status (default: true)                                                                             |
| `is_featured`           | boolean          | Featured flag                                                                                                |
| `is_no_follow`          | boolean          | NoFollow flag                                                                                                |
| `logo_url`              | string           | Logo image URL                                                                                               |
| `image_url`             | string           | Cover image URL                                                                                              |
| `logo_alt_text`         | string           | Logo alt text                                                                                                |
| `image_alt_text`        | string           | Cover image alt text                                                                                         |
| `additional_image_urls` | array            | Gallery images hosted elsewhere, as an array of URLs. **This is the writable one**                           |
| `video_url`             | string           | Video URL                                                                                                    |
| `video_thumbnail_url`   | string           | Video thumbnail URL                                                                                          |
| `votes`                 | integer          | Vote count                                                                                                   |
| `starts_at`             | datetime         | When the listing goes live. Hidden until then; omit to publish immediately                                   |
| `ends_at`               | datetime         | When the listing expires. Badged as expired or hidden afterwards (per directory setting); omit for no expiry |
| `seo_title`             | string           | SEO meta title                                                                                               |
| `seo_description`       | string           | SEO meta description                                                                                         |
| `schema`                | string           | JSON-LD schema markup                                                                                        |
| `head_html`             | string           | Custom HTML for the page `<head>` section (e.g. hreflang tags)                                               |

Custom field values can also be passed by using the custom field name as the key (e.g., `"pricing": "Free"`, `"founded_year": "2020"`).

<Warning>
  **`additional_image_urls` vs `additional_images_urls`** — these differ by a single letter and are not the same thing:

  * **`additional_image_urls`** (image, singular) is the **writable** field. Send gallery images hosted elsewhere as an array of URLs.
  * **`additional_images_urls`** (images, plural) is **read-only**, and appears in responses only. It is the listing's **complete** gallery: images uploaded through the dashboard (returned as CDN URLs) followed by everything in `additional_image_urls`.

  Read `additional_images_urls` when you want to render the gallery. Write `additional_image_urls` when you want to change it. Sending `additional_images_urls` does nothing.
</Warning>

<Note>
  When a `url` is provided, the API uses it for duplicate detection. If a listing with the same URL already exists in the directory, it will be returned as-is instead of creating a duplicate.
</Note>

**Response (201 Created):**

```json theme={null}
{
  "data": [
    {
      "id": 123,
      "name": "Listing 1",
      "slug": "listing-1",
      "description": "Description for listing 1",
      "url": "https://example1.com",
      "is_active": true,
      "is_featured": false,
      "categories": [
        { "id": 1, "title": "Category A", "parent_title": null }
      ],
      "tags": [
        { "id": 1, "title": "Tag 1", "slug": "tag-1" }
      ],
      "created_at": "2024-01-15T10:30:00.000000Z",
      "updated_at": "2024-01-15T10:30:00.000000Z"
    },
    {
      "id": 124,
      "name": "Listing 2",
      "slug": "listing-2",
      "description": "Description for listing 2",
      "is_active": true,
      "is_featured": true,
      "categories": [
        { "id": 3, "title": "Category C", "parent_title": null }
      ],
      "created_at": "2024-01-15T10:30:00.000000Z",
      "updated_at": "2024-01-15T10:30:00.000000Z"
    }
  ],
  "errors": [],
  "total_created": 2,
  "total_errors": 0
}
```

**Response with partial errors:**

```json theme={null}
{
  "data": [
    {
      "id": 123,
      "name": "Listing 1",
      "slug": "listing-1"
    }
  ],
  "errors": [
    {
      "index": 1,
      "name": "Listing 2",
      "error": "Error description"
    }
  ],
  "total_created": 1,
  "total_errors": 1
}
```

***

### Update Listing

Update an existing listing.

```http theme={null}
PUT /api/directories/{directory_id}/projects/{project_id}
```

**Parameters:**

* `directory_id` (integer, required): The ID of the directory
* `project_id` (integer, required): The ID of the listing

**Request Body:**

```json theme={null}
{
  "name": "Updated Example Tool",
  "description": "An updated description for the tool",
  "phone_number": "+1 (555) 987-6543",
  "email": "new@example.com",
  "address": "456 Oak Ave, San Francisco, CA",
  "latitude": 37.7749,
  "longitude": -122.4194,
  "social_links": {
    "twitter": "https://twitter.com/updated",
    "github": "https://github.com/updated"
  },
  "categories": [1],
  "tags": [1],
  "organizers": [5],
  "starts_at": "2026-08-01 09:00:00",
  "ends_at": "2026-09-01 09:00:00",
  "seo_title": "Updated Example Tool - Now Even Better",
  "seo_description": "The updated leading tool for developers.",
  "is_active": true,
  "is_featured": true,
  "pricing": "Free - $50/month"
}
```

<Note>
  Pass `ends_at: null` to remove an expiry date and make a listing permanent again.
</Note>

**Response:**

```json theme={null}
{
  "data": {
    "id": 123,
    "name": "Updated Example Tool",
    "slug": "updated-example-tool",
    "description": "An updated description for the tool",
    "content": null,
    "url": "https://example.com",
    "image_url": "https://example.com/images/updated.jpg",
    "logo_url": "https://example.com/logos/updated.png",
    "logo_alt_text": null,
    "image_alt_text": null,
    "additional_image_urls": [],
    "additional_images_urls": [],
    "video_url": null,
    "video_thumbnail_url": null,
    "phone_number": "+1 (555) 987-6543",
    "email": "new@example.com",
    "address": "456 Oak Ave, San Francisco, CA",
    "latitude": 37.7749,
    "longitude": -122.4194,
    "social_links": {
      "twitter": "https://twitter.com/updated",
      "github": "https://github.com/updated"
    },
    "categories": [
      {
        "id": 1,
        "title": "Web Development",
        "parent_title": null
      }
    ],
    "tags": [
      {
        "id": 1,
        "title": "Open Source",
        "slug": "open-source",
        "color": "#3B82F6",
        "text_color": "#FFFFFF",
        "is_active": true
      }
    ],
    "organizers": [
      { "id": 5, "name": "Acme Events", "slug": "acme-events" }
    ],
    "is_active": true,
    "is_featured": true,
    "is_no_follow": false,
    "is_claimed": false,
    "votes": 42,
    "seo_title": "Updated Example Tool - Now Even Better",
    "seo_description": "The updated leading tool for developers.",
    "og_image": null,
    "schema": null,
    "head_html": null,
    "created_at": "2024-01-15T10:30:00.000000Z",
    "updated_at": "2024-01-15T11:45:00.000000Z",
    "listing_url": "https://your-directify-domain.com/app/1/projects/123/edit",
    "pricing": "Free - $50/month"
  }
}
```

<Note>
  **Partial updates:** When updating a listing, you only need to send the fields you want to change. Fields that are omitted from the request body will remain unchanged. Importantly, if you omit `categories`, `tags`, or `organizers` from a PUT request, the existing assignments will be preserved. To explicitly clear them, send empty arrays: `"categories": []`, `"tags": []`, or `"organizers": []`.
</Note>

### Delete Listing

Delete a listing from the directory.

```http theme={null}
DELETE /api/directories/{directory_id}/projects/{project_id}
```

**Parameters:**

* `directory_id` (integer, required): The ID of the directory
* `project_id` (integer, required): The ID of the listing

**Response:**

```json theme={null}
{
  "message": "Project deleted successfully"
}
```
