# Delete all posts by a user on entire site with API?

**URL:** https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332
**Category:** Development
**Tags:** rest-api
**Created:** [April 4, 2024, 2:53am UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332 "2024-04-04T02:53:10Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![45thj5ej](https://avatars.discourse-cdn.com/v4/letter/4/34f0e0/32.png) [@45thj5ej](https://meta.discourse.org/u/45thj5ej)
#### Post date: [April 4, 2024, 2:53am UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/1 "2024-04-04T02:53:10Z")

</div>

How would I accomplish this? Can’t seem to really figure out what to do…

I’ve looked at:

> [@Reverse engineer the Discourse API](https://meta.discourse.org/t/reverse-engineer-the-discourse-api/20576):
>
> Discourse is backed by a complete JSON api. Anything you can do on the site you can also do using the JSON api. The API is documented at [docs.discourse.org](https://docs.discourse.org). You can also use the [discourse\_api](https://github.com/discourse/discourse_api) Ruby gem as a client library. However, not every endpoint is documented. To determine how to do something with the JSON API here are some steps you can follow. Example: recategorize a topic. Go to a topic and start editing a category: Open Chrome dev tools, switch to the Network tab, select …

[https://www.postman.com/api-evangelist/workspace/discourse/documentation/35240-c739e2a5-3ddf-4ffc-90e9-7dd579e8a55d?entity=request-35240-b041dc53-b653-4c46-b067-aee2b0a5cb46](https://www.postman.com/api-evangelist/workspace/discourse/documentation/35240-c739e2a5-3ddf-4ffc-90e9-7dd579e8a55d?entity=request-35240-b041dc53-b653-4c46-b067-aee2b0a5cb46)

but I don’t understand. The main thing I also don’t get is how to have each random ID gotten and then sent to be deleted since each ID is different…

---

<div class="post-metadata">

### Author: ![Lilly](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lilly/32/575047_2.png) [@Lilly](https://meta.discourse.org/u/Lilly)
#### Post date: [April 4, 2024, 4:26am UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/2 "2024-04-04T04:26:07Z")

</div>

> Lola, how does one delete all Discourse posts by a user using the API?

> [@Lola the Coding Assistant Bot](#):
>
> To delete all Discourse posts by a user through the API, follow these steps:
> 
> 1. **Get User Posts** : Fetch the list of all posts by the user.
> 2. **Delete Each Post** : Iterate through the list and delete each post.
> 
> ### 1. Get User Posts
> 
> **Endpoint** : `GET /posts.json`  
> **Parameters** : `username`: The username of the user.
> 
> ```plaintext
> curl -X GET "http://yourdiscourse.example.com/posts.json?username=targetUser" \
> -H "Api-Key: your_api_key" \
> -H "Api-Username: your_admin_username"
> 
> ```
> 
> ### 2. Delete Each Post
> 
> **Endpoint** : `DELETE /posts/{id}.json`
> 
> Loop through each post ID from step 1, and use the following command:
> 
> ```plaintext
> curl -X DELETE "http://yourdiscourse.example.com/posts/{post_id}.json" \
> -H "Api-Key: your_api_key" \
> -H "Api-Username: your_admin_username"
> 
> ```
> 
> Replace `{post_id}` with the actual post ID.
> 
> **Note** : Ensure you have the necessary permissions and are aware of the implications of bulk deleting posts. This action cannot be undone.

---

<div class="post-metadata">

### Author: ![Lilly](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lilly/32/575047_2.png) [@Lilly](https://meta.discourse.org/u/Lilly)
#### Post date: [April 4, 2024, 5:01am UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/3 "2024-04-04T05:01:27Z")

</div>

Btw I don’t believe there is a way to bulk delete via the API. I think you need to write a script to cycle through each post to delete. Depending on how many posts, rate limits could be an issue.

> Lola, how do I write a script to bulk delete Discourse posts?

> [@Lola the Coding Assistant Bot](#):
>
> To accomplish batch processing for deleting Discourse posts, you can use a scripting language like Python. Below is an example script that demonstrates how to automate the deletion of posts for a specific user. This script:
> 
> 1. Fetches posts by a specific user.
> 2. Iterates through the list of posts.
> 3. Deletes each post individually.
> 
> ### Example Python Script
> 
> ```python
> import requests
> 
> # Configuration
> discourse_base_url = 'http://yourdiscourse.example.com'
> api_key = 'your_api_key'
> api_username = 'your_admin_username'
> target_username = 'targetUser'
> 
> # Headers for API requests
> headers = {
> 'Api-Key': api_key,
> 'Api-Username': api_username
> }
> 
> def get_user_posts(username):
> """Fetch posts by a specific user."""
> posts = []
> url = f'{discourse_base_url}/posts.json?username={username}'
> response = requests.get(url, headers=headers)
> if response.status_code == 200:
> posts_data = response.json()
> posts = posts_data.get('latest_posts', [])
> return posts
> 
> def delete_post(post_id):
> """Delete a specific post."""
> url = f'{discourse_base_url}/posts/{post_id}.json'
> response = requests.delete(url, headers=headers)
> return response.status_code == 200
> 
> def main():
> # Fetch user posts
> posts = get_user_posts(target_username)
>     
> # Delete each post
> for post in posts:
> post_id = post.get('id')
> if delete_post(post_id):
> print(f'Successfully deleted post {post_id}')
> else:
> print(f'Failed to delete post {post_id}')
> 
> if __name__ == ' __main__':
> main()
> 
> ```
> 
> ### Explanation
> 
> - **Configuration** : Set your Discourse instance’s base URL, API key, API username, and the target username whose posts you want to delete.
> - **`get_user_posts` Function** : Fetches posts by the specified user. Adjust the URL or parameters as needed based on your Discourse setup.
> - **`delete_post` Function** : Deletes a post by its ID.
> - **`main` Function** : Orchestrates the process by first fetching the user’s posts and then deleting each one.
> 
> ### Important Notes
> 
> - **Rate Limits** : Be mindful of Discourse’s API rate limits. If you’re deleting a large number of posts, consider adding delays between requests.
> - **Permissions** : Ensure your API key has the necessary permissions to delete posts.
> - **Testing** : Test the script in a safe environment before running it in production.
> 
> This script provides a basic framework. Depending on your specific needs, you might need to adjust the API endpoints, parameters, or add error handling and logging.

BTW, Why are you using API to do this?

I think it can also be done as a rake task from command line.

---

<div class="post-metadata">

### Author: ![45thj5ej](https://avatars.discourse-cdn.com/v4/letter/4/34f0e0/32.png) [@45thj5ej](https://meta.discourse.org/u/45thj5ej)
#### Post date: [April 4, 2024, 2:08pm UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/4 "2024-04-04T14:08:45Z")

</div>

Thank you, will try this out sometime today! So, can that rake stuff be done as a normal user? I would like this all to be done as a normal user who isn’t staff.

> [@Lilly](#):
>
> I think you need to write a script

This.

When I ran the python script below:

```plaintext
import requests
# Configuration
discourse_base_url = 'https://forums.mysite.me/'
api_key = 'eb4065d1dergrgh45h45h54h45reg4g70cbeb274bf7f87'
api_username = 'username'
target_username = 'targetusername'

# Headers for API requests
headers = {
    'eb4065d1dergrgh45h45h54h45reg4g70cbeb274bf7f87': api_key,
    'B': api_username
}

def get_user_posts(username):
    """Fetch posts by a specific user."""
    posts = []
    url = f'{discourse_base_url}/posts.json?username={username}'
    response = requests.get(url, headers=headers)
    if response.status_code == 200:
        posts_data = response.json()
        posts = posts_data.get('latest_posts', [])
    return posts

def delete_post(post_id):
    """Delete a specific post."""
    url = f'{discourse_base_url}/posts/{post_id}.json'
    response = requests.delete(url, headers=headers)
    return response.status_code == 200

def main():
    # Fetch user posts
    posts = get_user_posts(target_username)
    
    # Delete each post
    for post in posts:
        post_id = post.get('id')
        if delete_post(post_id):
            print(f'Successfully deleted post {post_id}')
        else:
            print(f'Failed to delete post {post_id}')

if __name__ == ' __main__':
    main()

```

On my terminal/console, I get:

```plaintext
Failed to delete post 73
Failed to delete post 71
Failed to delete post 69
Failed to delete post 60
Failed to delete post 47

```

I tried 2 API keys I made. One with only me who can use it and one will all users. Both times, used the Global option. Same result.

---

<div class="post-metadata">

### Author: ![45thj5ej](https://avatars.discourse-cdn.com/v4/letter/4/34f0e0/32.png) [@45thj5ej](https://meta.discourse.org/u/45thj5ej)
#### Post date: [April 4, 2024, 2:20pm UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/6 "2024-04-04T14:20:44Z")

</div>

![bbbb](https://global.discourse-cdn.com/meta/original/4X/4/4/0/4405cd167c9e72ee0aedabfb72b6c7a264aabe3d.png)

I’m sorry, but this made me audibly laugh. I needed that today this morning, haha.

---

<div class="post-metadata">

### Author: ![blake](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/blake/32/157322_2.png) [@blake](https://meta.discourse.org/u/blake)
#### Post date: [April 4, 2024, 2:53pm UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/7 "2024-04-04T14:53:11Z")

</div>

> [@45thj5ej](#):
>
> I would like this all to be done as a normal user who isn’t staff.

That’s not going to work. Even for API requests. After a certain point users can’t delete their own posts.

Usually the best option is to anonymize all posts by a user if they no longer want to be apart of the forum. This will simply change their username to something random so that their posts are no longer associated with them.

---

<div class="post-metadata">

### Author: ![45thj5ej](https://avatars.discourse-cdn.com/v4/letter/4/34f0e0/32.png) [@45thj5ej](https://meta.discourse.org/u/45thj5ej)
#### Post date: [April 4, 2024, 2:55pm UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/9 "2024-04-04T14:55:22Z")

</div>

@Lilly Saw the reply ya deleted. So, what about making it work for myself or another Admin user? Not sure if ya saw, but I edited my above post to include the result I’m getting when using that script.

I guess same can be asked to you too, @blake, haha.

> [@blake](#):
>
> is to anonymize all posts by a user if they no longer want to be apart of the forum

This is just for testing atm.

---

<div class="post-metadata">

### Author: ![Lilly](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lilly/32/575047_2.png) [@Lilly](https://meta.discourse.org/u/Lilly)
#### Post date: [April 4, 2024, 2:56pm UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/10 "2024-04-04T14:56:03Z")

</div>

I deleted because Blake answered already.

---

<div class="post-metadata">

### Author: ![blake](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/blake/32/157322_2.png) [@blake](https://meta.discourse.org/u/blake)
#### Post date: [April 4, 2024, 3:10pm UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/12 "2024-04-04T15:10:14Z")

</div>

> [@45thj5ej](#):
>
> but I edited my above post to include the result I’m getting when using that script.

You can try this curl request for your post id

```plaintext
curl -i -sS -X DELETE "http://localhost:4200/posts/<post-id>.json" \
-H "Content-Type: multipart/form-data" \
-H "Api-Key: key" \
-H "Api-Username: username"

```

To see if it returns a different error but likely it will just say “An Error Occurred” ☹

 ![CleanShot 2024-04-04 at 09.10.46@2x](https://global.discourse-cdn.com/meta/original/4X/d/c/2/dc296bf2cf5f56bea94c9901a6b41f9366fd4fda.png)

Looks like there are some site settings around users deleting posts that you can tweak. Also looks like there might be a “Delete all posts” button somewhere.

---

<div class="post-metadata">

### Author: ![45thj5ej](https://avatars.discourse-cdn.com/v4/letter/4/34f0e0/32.png) [@45thj5ej](https://meta.discourse.org/u/45thj5ej)
#### Post date: [April 4, 2024, 3:19pm UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/13 "2024-04-04T15:19:53Z")

</div>

Trying the curl command above:

```plaintext
curl -X GET "https://forums.mysite.me/posts.json?username=targetusername" \
-H "Api-Key: eb4065d56u7u6u65u54y54uy566575w4yer343434ac8a770cbeb274bf7f87" \
-H "Api-Username: AdminUsername"

```

So, it ran and fetched me the posts…but then I got this:

```plaintext
C:\Users\User>-H "Api-Key: eb4065d56u7u6u65u54y54uy566575w4yer343434ac8a770cbeb274bf7f87"
'-H' is not recognized as an internal or external command,
operable program or batch file.

C:\Users\User>-H "Api-Username: AdminUsername"
'-H' is not recognized as an internal or external command,
operable program or batch file.

```

Is this normal or why is that happening?

---

<div class="post-metadata">

### Author: ![blake](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/blake/32/157322_2.png) [@blake](https://meta.discourse.org/u/blake)
#### Post date: [April 4, 2024, 3:23pm UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/14 "2024-04-04T15:23:11Z")

</div>

The curl command you entered is not the same as the one from [above](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/12).

Looks like you are using the Windows cmd terminal, maybe it doesn’t like the `\` separating lines. You can just remove them so it is all on one line.

---

<div class="post-metadata">

### Author: ![45thj5ej](https://avatars.discourse-cdn.com/v4/letter/4/34f0e0/32.png) [@45thj5ej](https://meta.discourse.org/u/45thj5ej)
#### Post date: [April 4, 2024, 3:24pm UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/15 "2024-04-04T15:24:18Z")

</div>

Tried it without the slashes already. Making it all one line fixed it, thanks!

> [@blake](#):
>
> The curl command you entered is not the same as the one from [above](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/12).

Sorry, meant the one Lilly shared.

---

<div class="post-metadata">

### Author: ![blake](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/blake/32/157322_2.png) [@blake](https://meta.discourse.org/u/blake)
#### Post date: [April 4, 2024, 3:29pm UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/17 "2024-04-04T15:29:19Z")

</div>

![CleanShot 2024-04-04 at 09.26.15@2x](https://global.discourse-cdn.com/meta/original/4X/e/7/0/e709188a69c0e67fdeffbcc312f719f95bff1bee.png)

Looks like there is an endpoint to batch delete all posts for a user.

It’s triggered by this button on the users page from the admin dashboard

 ![CleanShot 2024-04-04 at 09.28.07@2x](https://global.discourse-cdn.com/meta/original/4X/d/1/4/d14fd3e9f3ee7b8622f56fcc22855130307a45e7.png)

---

<div class="post-metadata">

### Author: ![45thj5ej](https://avatars.discourse-cdn.com/v4/letter/4/34f0e0/32.png) [@45thj5ej](https://meta.discourse.org/u/45thj5ej)
#### Post date: [April 4, 2024, 3:31pm UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/18 "2024-04-04T15:31:56Z")

</div>

> [@blake](#):
>
> ```plaintext
> curl -i -sS -X DELETE "http://localhost:4200/posts/<post-id>.json" \
> -H "Content-Type: multipart/form-data" \
> -H "Api-Key: key" \
> -H "Api-Username: username"
> 
> ```

So, instead of manually having to enter a post-id (because at that point, I might as well go on the actual site and just hit the delete buttons, right?), how can that auto-get filled in for each post?

> [@blake](#):
>
> Looks like there is an endpoint to batch delete all posts for a user.

Oh, sweet! So I would just make the URL:  
`http://mysite.com/admin/users/79/delete_posts_batch` ?  
Or, what is the “79” here? a user ID I assume that you get when you go to the user’s profile?

---

<div class="post-metadata">

### Author: ![Arkshine](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/arkshine/32/298682_2.png) [@Arkshine](https://meta.discourse.org/u/Arkshine)
#### Post date: [April 4, 2024, 3:37pm UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/19 "2024-04-04T15:37:19Z")

</div>

> [@45thj5ej](#):
>
> Oh, sweet! So I would just make the URL:  
> `http://mysite.com/admin/users/79/delete_posts_batch` ?  
> Or, what is the “79” here? a user ID I assume that you get when you go to the user’s profile?

Yes, that’s the user’s ID, and it’s a `PUT` request. It returns the number of deleted posts.

---

<div class="post-metadata">

### Author: ![45thj5ej](https://avatars.discourse-cdn.com/v4/letter/4/34f0e0/32.png) [@45thj5ej](https://meta.discourse.org/u/45thj5ej)
#### Post date: [April 4, 2024, 3:38pm UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/20 "2024-04-04T15:38:32Z")

</div>

Oh, so I wouldn’t do?

```plaintext
curl -i -sS -X DELETE "http://forums.mysite.com/admin/users/0/delete_posts_batch" \
-H "Content-Type: multipart/form-data" \
-H "Api-Key: eb4065d45745678u564754y4545y545445674y34545y50cbeb274bf7f87" \
-H "Api-Username: AdminUsername"

```

---

<div class="post-metadata">

### Author: ![Arkshine](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/arkshine/32/298682_2.png) [@Arkshine](https://meta.discourse.org/u/Arkshine)
#### Post date: [April 4, 2024, 3:40pm UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/21 "2024-04-04T15:40:59Z")

</div>

Not `DELETE` but `PUT` request. Also, there are no form parameters here.

---

<div class="post-metadata">

### Author: ![supermathie](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/supermathie/32/507518_2.png) [@supermathie](https://meta.discourse.org/u/supermathie)
#### Post date: [April 4, 2024, 3:49pm UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/22 "2024-04-04T15:49:32Z")

</div>

If it’s an _admin_ performing deleting, there’s an admin operation to :garbage: Delete all posts for a user.

You might need to adjust other settings first to permit this (`delete all posts max`).

> [@45thj5ej](#):
>
> `eb4065d45745678u564754y4545y545445674y34545y50cbeb274bf7f87`

I certainly hope this isn’t the actual API key for your site. As previously noted, it’s pretty easy to figure out what your site actually is.

You should consider this key burned and rotate it.

---

<div class="post-metadata">

### Author: ![45thj5ej](https://avatars.discourse-cdn.com/v4/letter/4/34f0e0/32.png) [@45thj5ej](https://meta.discourse.org/u/45thj5ej)
#### Post date: [April 4, 2024, 4:59pm UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/23 "2024-04-04T16:59:58Z")

</div>

So just take that form line out completely?

EDIT: I kept the line in and just changed “DELETE” to “PUT” and it worked.

---

<div class="post-metadata">

### Author: ![discord\_user1031](https://avatars.discourse-cdn.com/v4/letter/d/b38774/32.png) [@discord\_user1031](https://meta.discourse.org/u/discord_user1031)
#### Post date: [May 30, 2024, 4:18pm UTC](https://meta.discourse.org/t/delete-all-posts-by-a-user-on-entire-site-with-api/302332/24 "2024-05-30T16:18:45Z")

</div>

> [@45thj5ej](#):
>
> pt the line in and just changed “DE

Delete posts using API is failing for me with 403 error with body {“errors”:[“You are not permitted to view the requested resource.”],“error\_type”:“invalid\_access”}

Any idea what the fix could be?
