WordPress stores every uploaded file as a post with post_type = 'attachment' and post_status = 'inherit'. This means the full power of WP_Query is available for querying the Media Library — filtering by MIME type, parent post, date, and custom meta including ACF fields attached to media. Most developers know get_attached_media() for retrieving files attached to a specific post, but WP_Query gives much finer control: retrieve all PDFs uploaded in the last 30 days, query images with a specific attachment meta value, or find all videos not attached to any post. This is useful for building custom media galleries, document libraries, download pages, and audit tools that need to report on orphaned media. The key difference from regular post queries is that you must set 'post_status' => 'inherit' and 'post_type' => 'attachment'.
Problem: You need to query the WordPress Media Library for specific files — all images in a gallery, all PDFs, all attachments belonging to a specific post, or all unattached media — using the standard WordPress query API.
Solution: Use WP_Query with 'post_type' => 'attachment' and 'post_status' => 'inherit'. Filter by MIME type with 'post_mime_type', by parent post with 'post_parent', or by attachment meta with 'meta_query'.
<?php
// ── All images attached to a specific post ────────────────────────────
$attached_images = new WP_Query( [
'post_type' => 'attachment',
'post_status' => 'inherit',
'post_parent' => 42, // parent post ID
'post_mime_type' => 'image', // 'image', 'image/jpeg', 'video', 'application/pdf'
'posts_per_page' => -1,
'orderby' => 'menu_order',
'order' => 'ASC',
] );
// ── All PDFs in the Media Library (unattached or attached) ────────────
$pdfs = new WP_Query( [
'post_type' => 'attachment',
'post_status' => 'inherit',
'post_parent' => null, // null = all, 0 = unattached only
'post_mime_type' => 'application/pdf',
'posts_per_page' => 20,
'orderby' => 'date',
'order' => 'DESC',
] );
// ── Images uploaded in the last 30 days ───────────────────────────────
$recent_media = new WP_Query( [
'post_type' => 'attachment',
'post_status' => 'inherit',
'post_mime_type' => 'image',
'date_query' => [ [ 'after' => '30 days ago', 'inclusive' => true ] ],
'posts_per_page' => 50,
] );
// ── Loop and output ───────────────────────────────────────────────────
if ( $pdfs->have_posts() ) {
while ( $pdfs->have_posts() ) {
$pdfs->the_post();
printf(
'<a href="%s" download>%s</a>',
esc_url( wp_get_attachment_url( get_the_ID() ) ),
esc_html( get_the_title() )
);
}
wp_reset_postdata();
}
Using the simpler get_attached_media() helper when you only need files from one post:
<?php
// Returns an array of WP_Post objects (attachments)
$images = get_attached_media( 'image', $post_id );
$videos = get_attached_media( 'video', $post_id );
foreach ( $images as $image ) {
echo wp_get_attachment_image( $image->ID, 'medium' );
}
NOTE: 'post_parent' => 0 queries for unattached media (files not linked to any post). This is useful for finding and cleaning up orphaned uploads that are taking up disk space. However, be careful when deleting — some plugins and themes store images in the Media Library without attaching them to posts, intentionally using post_parent = 0. Always preview results before bulk-deleting unattached media.